Skip to content

5. Interfaces gráficas

Aquí nos proponemos mostrar cómo construir interfaces gráficas con JAVA. En primer lugar, veremos cuáles son las clases básicas que nos permiten crear una interfaz gráfica. En un primer momento, no utilizaremos ninguna herramienta de generación automática. Posteriormente, utilizaremos JBuilder, una herramienta de desarrollo de Borland/Inprise que facilita el desarrollo de aplicaciones Java y, en particular, la creación de interfaces gráficas.

5.1. Fundamentos de las interfaces gráficas

5.1.1. Una ventana sencilla

Consideremos el siguiente código:


// clases importadas
import javax.swing.*;
import java.awt.*;
    
    // la clase formulario
  public class form1 extends JFrame {
    
      // el constructor
      public form1() {
            // título de la ventana
            this.setTitle("Mon premier formulaire");
            // dimensiones de la ventana
            this.setSize(new Dimension(300,100));
      }//constructor
  
      // función de prueba
      public static void main(String[] args) {
            // se muestra el formulario
            new form1().setVisible(true);
      }
  }//clase

Al ejecutar el código anterior, aparece la siguiente ventana:

Image

Una interfaz gráfica suele derivarse de la clase base JFrame:


  public class form1 extends JFrame {

La clase base JFrame define una ventana básica con botones para cerrarla, ampliarla o reducirla, un tamaño ajustable, etc., y gestiona los eventos de estos objetos gráficos. Aquí especializamos la clase base asignándole un título y su anchura (300 píxeles) y altura (100 píxeles). Esto se hace en su constructor:


      // el constructor
      public form1() {
            // título de la ventana
            this.setTitle("Mon premier formulaire");
            // dimensiones de la ventana
            this.setSize(new Dimension(300,100));
      }//constructor

El título de la ventana se establece mediante el método setTitle y sus dimensiones mediante el método setSize. Este método acepta como parámetro un objeto Dimension(largeur,hauteur), donde largeur y hauteur son la anchura y la altura de la ventana, expresadas en píxeles.

El método main inicia la aplicación gráfica de la siguiente manera:


            new form1().setVisible(true);

A continuación, se crea un formulario de tipo form1 (new form1()) y se muestra (setVisible(true)); después, la aplicación se pone a la escucha de los eventos que se producen en el formulario (clics, movimientos del ratón, ...) y ejecuta aquellos que el formulario gestiona. En este caso, nuestro formulario no gestiona otros eventos aparte de los que gestiona la clase base JFrame (clics en los botones de cierre, maximizar/minimizar, cambio de tamaño de la ventana, desplazamiento de la ventana, etc.).

Cuando se prueba este programa ejecutándolo desde una ventana de DOS mediante:

java form1

para ejecutar el archivo form1.class, se observa que, al cerrar la ventana que se ha mostrado, no se «recupera el control» en la ventana de DOS, como si el programa no hubiera finalizado. Y así es, efectivamente. La ejecución del programa se lleva a cabo de la siguiente manera:

  • al principio, se inicia un primer hilo de ejecución para ejecutar el método main
  • cuando este crea el formulario y lo muestra, se crea un segundo hilo para gestionar específicamente los eventos relacionados con el formulario
  • tras esta creación y, en nuestro ejemplo, el hilo del método main finaliza y solo queda entonces el hilo de ejecución de la interfaz gráfica.
  • Al cerrar la ventana, esta desaparece, pero no interrumpe el hilo en el que se estaba ejecutando
  • Por el momento, nos vemos obligados a detener este hilo pulsando Ctrl+C en la ventana de DOS desde la que se inició la ejecución del programa.

Comprobemos la existencia de dos subprocesos separados: uno en el que se ejecuta el método main y otro en el que se ejecuta la ventana gráfica:


// clases importadas
import javax.swing.*;
import java.awt.*;
import java.io.*;

// la clase del formulario
 public class form1 extends JFrame {
    
// el constructor
 public form1() {
   // título de la ventana
   this.setTitle("Mon premier formulaire");
   // dimensiones de la ventana
   this.setSize(new Dimension(300,100));
}//constructor
  
// función de prueba
 public static void main(String[] args) {
   // seguimiento
   System.out.println("Début du thread main");
   // se muestra el formulario
   new form1().setVisible(true);
   // seguimiento
   System.out.println("Fin du thread main");  
}//principal
}//clase

La ejecución da los siguientes resultados:

Image

Se puede observar que el hilo main ha finalizado, mientras que la ventana sigue abierta. El hecho de cerrar la ventana no detiene el hilo en el que se estaba ejecutando. Para detener este hilo, volvemos a pulsar Ctrl+C en la ventana de DOS.

Para concluir este ejemplo, cabe señalar los paquetes importados:

  • javax.swing para la clase JFrame
  • java.awt para la clase Dimension

5.1.2. Gestionar un evento

En el ejemplo anterior, tendríamos que gestionar nosotros mismos el cierre de la ventana para que, cuando se produzca, se detenga la aplicación, algo que por el momento no se hace. Para ello, debemos crear un objeto que «escuche» los eventos que se producen en la ventana y detecte el evento «cierre de la ventana». A este objeto se le denomina «listener» o gestor de eventos. Existen diferentes tipos de gestores para los distintos eventos que pueden producirse en los componentes de una interfaz gráfica. Para el componente JFrame, el listener se llama WindowListener y es una interfaz que define los siguientes métodos (véase la documentación de Java)

Resumen de métodos
 
void
windowActivated(WindowEvent e)
          La ventana se convierte en la ventana activa
 
void
windowClosed(WindowEvent e)
          La ventana se ha cerrado
 
void
windowClosing(WindowEvent e)
          El usuario o el programa ha solicitado el cierre de la ventana
 
void
windowDeactivated(WindowEvent e)
          La ventana ya no es la ventana activa
 
void
windowDeiconified(WindowEvent e)
          La ventana pasa del estado minimizado al estado normal
 
void
windowIconified(WindowEvent e)
          La ventana pasa del estado normal al estado minimizado
 
void
windowOpened(WindowEvent e)
          La ventana se hace visible por primera vez
 

Por lo tanto, hay siete eventos que se pueden gestionar. Todos los controladores reciben como parámetro un objeto de tipo WindowEvent que, por el momento, ignoramos. El evento que nos interesa aquí es el cierre de la ventana, evento que deberá ser gestionado por el método windowClosing. Para gestionar este evento, podemos crear un objeto Windowlistener utilizando una clase anónima de la siguiente manera:


   // creación de un gestor de eventos
   WindowListener win=new WindowListener(){
     public void windowActivated(WindowEvent e){}
     public void windowClosed(WindowEvent e){}
     public void windowClosing(WindowEvent e){System.exit(0);}
     public void windowDeactivated(WindowEvent e){}
     public void windowDeiconified(WindowEvent e){}
     public void windowIconified(WindowEvent e){}
     public void windowOpened(WindowEvent e){}
   };//definición de Win

Nuestro gestor de eventos, que implementa la interfaz WindowListener, debe definir los siete métodos de dicha interfaz. Como solo queremos gestionar el cierre de la ventana, solo definimos código para el método windowClosing. Cuando se produzcan los demás eventos, se nos notificará, pero no haremos nada. ¿Qué haremos cuando se nos notifique que la ventana se está cerrando (windowClosing)? Detendremos la aplicación:


     public void windowClosing(WindowEvent e){System.exit(0);}

Aquí tenemos un objeto capaz de gestionar los eventos de una ventana en general. ¿Cómo lo asociamos a una ventana concreta? La clase JFrame tiene un método addWindowListener(WindowListener win) que permite asociar un gestor de eventos de «ventana» a una ventana concreta. Así pues, aquí, en el constructor de la ventana, escribiremos:


   // creación de un gestor de eventos
   WindowListener win=new WindowListener(){
     public void windowActivated(WindowEvent e){}
     public void windowClosed(WindowEvent e){}
     public void windowClosing(WindowEvent e){System.exit(0);}
     public void windowDeactivated(WindowEvent e){}
     public void windowDeiconified(WindowEvent e){}
     public void windowIconified(WindowEvent e){}
     public void windowOpened(WindowEvent e){}
   };//definición de Windows
    // Este gestor de eventos se encargará de gestionar los eventos de la ventana actual
   this.addWindowListener(win);

El programa completo es el siguiente:


// clases importadas
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;

// la clase «formulario»
 public class form2 extends JFrame {
    
// el constructor
 public form2() {
   // título de la ventana
   this.setTitle("Mon premier formulaire");
   // dimensiones de la ventana
   this.setSize(new Dimension(300,100));
   // creación de un gestor de eventos
   WindowListener win=new WindowListener(){
     public void windowActivated(WindowEvent e){}
     public void windowClosed(WindowEvent e){}
     public void windowClosing(WindowEvent e){System.exit(0);}
     public void windowDeactivated(WindowEvent e){}
     public void windowDeiconified(WindowEvent e){}
     public void windowIconified(WindowEvent e){}
     public void windowOpened(WindowEvent e){}
   };//definición de Windows
   // este gestor de eventos gestionará los eventos de la ventana actual
   this.addWindowListener(win);
}//constructor
  
// función de prueba
 public static void main(String[] args) {
   // se muestra el formulario
   new form2().setVisible(true);
}//main
}//clase

El paquete java.awt.event contiene la interfaz WindowListener. Al ejecutar este programa y cerrar la ventana que se ha mostrado, se observa en la ventana de DOS desde donde se ha iniciado el programa que este ha finalizado su ejecución, algo que antes no ocurría.

En nuestro programa, la creación del objeto encargado de gestionar los eventos de la ventana resulta un poco engorrosa, ya que nos vemos obligados a definir métodos incluso para eventos que no queremos gestionar. En este caso, en lugar de utilizar la interfaz WindowListener, podemos utilizar la clase WindowAdapter. Esta implementa la interfaz WindowListener, con siete métodos vacíos. Al derivar la clase WindowAdapter y redefinir únicamente los métodos que nos interesan, obtenemos el mismo resultado que con la interfaz WindowListener, pero sin necesidad de definir los métodos que no nos interesan. La secuencia

  • definición del gestor de eventos
  • asociación del gestor a la ventana

puede llevarse a cabo de la siguiente manera en nuestro ejemplo:


   // creación de un gestor de eventos
   WindowAdapter win=new WindowAdapter(){
     public void windowClosing(WindowEvent e){System.exit(0);}
   };//definición de Win
   // este gestor de eventos gestionará los eventos de la ventana actual
   this.addWindowListener(win);

Aquí utilizamos una clase anónima que deriva de la clase WindowAdapter y redefine su método windowClosing. El programa queda entonces así:


// clases importadas
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;

// la clase «formulario»
 public class form2 extends JFrame {
    
// el constructor
 public form2() {
   // título de la ventana
   this.setTitle("Mon premier formulaire");
   // dimensiones de la ventana
   this.setSize(new Dimension(300,100));
   // creación de un gestor de eventos
   WindowAdapter win=new WindowAdapter(){
     public void windowClosing(WindowEvent e){System.exit(0);}
   };//definición de Windows
   // este gestor de eventos gestionará los eventos de la ventana actual
   this.addWindowListener(win); }//constructor
  
// función de prueba
 public static void main(String[] args) {
   // se muestra el formulario
   new form2().setVisible(true);
}//main
}//clase

Ofrece los mismos resultados que el programa anterior, pero es más sencillo de escribir.

5.1.3. Un formulario con un botón

Ahora añadamos un botón a nuestra ventana:


// clases importadas
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;

// la clase del formulario
 public class form3 extends JFrame {
 
     // un botón
  JButton btnTest=null;
  Container conteneur=null;
  
  // el constructor
  public form3() {
     // título de la ventana
    this.setTitle("Formulaire avec bouton");
     // dimensiones de la ventana
    this.setSize(new Dimension(300,100));
    // creación de un gestor de eventos
    WindowAdapter win=new WindowAdapter(){
     public void windowClosing(WindowEvent e){System.exit(0);}
    };//definición de Windows
     // este gestor de eventos gestionará los eventos de la ventana actual
    this.addWindowListener(win);
     // se recupera el contenedor de la ventana
    conteneur=this.getContentPane();
     // se elige un gestor de maquetación de los componentes de este contenedor
    conteneur.setLayout(new FlowLayout());
    // se crea un botón
    btnTest=new JButton();
    // se establece su texto
    btnTest.setText("Test");
    // se añade el botón al contenedor
    conteneur.add(btnTest);
  }//constructor
  
  // función de prueba
 public static void main(String[] args) {
    // se muestra el formulario
   new form3().setVisible(true);
}//main
}//clase

Una ventana JFrame tiene un contenedor en el que se pueden colocar componentes gráficos (botones, casillas de selección, listas desplegables, etc.). Se puede acceder a este contenedor mediante el método getContentPane de la clase JFrame:


  Container conteneur=null;
..........
     // se recupera el contenedor de la ventana
    conteneur=this.getContentPane();

Cada componente se coloca en el contenedor mediante el método add de la clase Container. Así, para colocar el componente C en el objeto conteneur anterior, se escribirá:

conteneur.add(C)

¿Dónde se coloca este componente en el contenedor? Existen varios gestores de disposición de componentes con el nombre XXXLayout, siendo XXX igual, por ejemplo, a Border, Flow, ... Cada gestor de disposición tiene sus propias características. Por ejemplo, el gestor FlowLayout dispone los componentes en línea, comenzando por la parte superior del formulario. Cuando se ha completado una línea, los componentes se colocan en la línea siguiente. Para asociar un gestor de formato a una ventana JFrame, se utiliza el método setLayout de la clase JFrame con la siguiente sintaxis:

    setLayout(objet XXXLayout);

Así, en nuestro ejemplo, para asociar un gestor de tipo FlowLayout a la ventana, hemos escrito:


     // se selecciona un gestor de formato para los componentes de este contenedor
    conteneur.setLayout(new FlowLayout());

Se puede prescindir del gestor de diseño y escribir:

    setLayout(null);

En este caso, habrá que indicar las coordenadas exactas del componente en el contenedor en el formato (x, y, ancho, alto), donde (x, y) son las coordenadas de la esquina superior izquierda del componente en el contenedor. Este es el método que utilizaremos con mayor frecuencia a partir de ahora.

Ahora sabemos cómo se colocan los componentes en el contenedor (add) y dónde (setLayout). Solo nos queda conocer los

componentes que se pueden colocar en un contenedor. Aquí colocamos un botón modelado por la clase javax.swing.JButton:



  JButton btnTest=null;
..........
     // se crea un botón
    btnTest=new JButton();
    // se establece su texto
    btnTest.setText("Test");
    // se añade el botón al contenedor
    conteneur.add(btnTest);

Al ejecutar este programa, aparece la siguiente ventana:

Image

Si se cambia el tamaño del formulario anterior, se activa automáticamente el gestor de disposición del contenedor para reubicar los componentes:

Image

Este es el principal interés de los gestores de disposición: mantener una disposición coherente de los componentes a medida que cambia el tamaño del contenedor. Utilicemos el gestor de disposición null para ver la diferencia. Ahora, el botón se coloca en el contenedor mediante las siguientes instrucciones:


// Se selecciona un gestor de formato de componentes en este contenedor
conteneur.setLayout(null);
    // se crea un botón
btnTest=new JButton();
    // se establece su texto
btnTest.setText("Test");
    // se establece su ubicación y sus dimensiones
btnTest.setBounds(10,20,100,20);
     // se añade el botón al contenedor
conteneur.add(btnTest);

Aquí colocamos explícitamente el botón en el punto (10,20) del formulario y fijamos sus dimensiones en 100 píxeles de ancho y 20 píxeles de alto. La nueva ventana queda así:

Image

Si se cambia el tamaño de la ventana, el botón permanece en el mismo lugar.

Image

Si hacemos clic en el botón Test, no ocurre nada. De hecho, aún no hemos asociado ningún controlador de eventos al botón. Para conocer el tipo de controladores de eventos disponibles para un componente determinado, podemos buscar en la definición de su clase los métodos addXXXListener que permiten asociar un controlador de eventos al componente. La clase javax.swing.JButton deriva de la clase javax.swing.AbstractButton, en la que se encuentran los siguientes métodos:

Resumen de métodos
 
void
addActionListener(ActionListener l)
 
void
addChangeListener(ChangeListener l)
 
void
addItemListener(ItemListener l)
 

En este caso, hay que consultar la documentación para saber qué gestor de eventos se encarga del clic en el botón. Se trata de la interfaz ActionListener. Esta solo define un método:

Resumen de métodos
 
void
actionPerformed(ActionEvent e)
 

El método recibe un parámetro ActionEvent que, por el momento, ignoraremos. Para gestionar el clic en el botón btntest de nuestro programa, primero le asignamos un gestor de eventos:


        btnTest.addActionListener(new ActionListener()
         {
                public void actionPerformed(ActionEvent evt){
                  btnTest_clic(evt);
              }
            }//clase anónima
    );//gestor de eventos

Aquí, el método actionPerformed hace referencia al método btnTest_clic, que definimos de la siguiente manera:


public void btnTest_clic(ActionEvent evt){
  // seguimiento de la consola
System.out.println("clic sur bouton");
}//btnTest_click

Cada vez que el usuario hace clic en el botón Test, se escribe un mensaje en la consola. Así lo muestra la siguiente ejecución:

Image

El programa completo es el siguiente:


// clases importadas
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.awt.event.*;

// la clase formulario
 public class form4 extends JFrame {
 
     // un botón
  JButton btnTest=null;
  Container conteneur=null;
  
  // el constructor
  public form4() {
     // título de la ventana
    this.setTitle("Formulaire avec bouton");
     // dimensiones de la ventana
    this.setSize(new Dimension(300,100));
    // creación de un gestor de eventos
    WindowAdapter win=new WindowAdapter(){
     public void windowClosing(WindowEvent e){System.exit(0);}
    };//definición de Windows
     // este gestor de eventos gestionará los eventos de la ventana actual
    this.addWindowListener(win);
     // se recupera el contenedor de la ventana
    conteneur=this.getContentPane();
     // se elige un gestor de maquetación de los componentes de este contenedor
    conteneur.setLayout(null);
    // se crea un botón
    btnTest=new JButton();
    // se establece su texto
    btnTest.setText("Test");
    // se establece su ubicación y sus dimensiones
    btnTest.setBounds(10,20,100,20);
     // se le asocia un gestor de eventos
    btnTest.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent evt){
              btnTest_clic(evt);
          }
        }//clase anónima
    );//gestor de eventos
     // se añade el botón al contenedor
    conteneur.add(btnTest);
  }//constructor

    public void btnTest_clic(ActionEvent evt){
       // seguimiento de consola
    System.out.println("clic sur bouton");
  }//btnTest_click
    
  // función de prueba
 public static void main(String[] args) {
    // se muestra el formulario
   new form4().setVisible(true);
}//principal
}//clase

5.1.4. Los gestores de eventos

Los principales componentes de Swing que vamos a presentar son las ventanas (JFrame), los botones (JButton), las casillas de selección (JCheckBox), los botones de opción (JButtonRadio), los menús desplegables (JComboBox), las listas (JList), los controles deslizantes (JScrollBar), las etiquetas (JLabel), los campos de entrada de una línea (JTextField) o de varias líneas (JTextArea), los menús (JMenuBar), los elementos de menú (JMenuItem).

Las siguientes tablas recogen una lista de algunos gestores de eventos y los eventos a los que están vinculados.

Controlador
Componente(s)
Método de registro
Evento
ActionListener
JButton, JCheckbox, JButtonRadio, JMenuItem
public void addActionListener(ActionListener)
clic en el botón, la casilla de verificación, el botón de opción o el elemento del menú
 
JTextField
ItemListener
JComboBox, JList
public void addItemListener(ItemListener)
El elemento seleccionado ha cambiado
InputMethodListener
JTextField, JTextArea
public void addMethodInputListener(InputMethodListener)
El texto del campo de entrada ha cambiado o el cursor de entrada ha cambiado de posición
CaretListener
JTextField, JTextArea
public void addcaretListener(CaretListener)
El cursor de entrada ha cambiado de posición
AdjustmentListener
JScrollBar
public void addAdjustmentListener(AdjustmentListener)
El valor del variador ha cambiado
MouseMotionListener
 
public void addMouseMotionListener(MouseMotionListener)
el ratón se ha movido
WindowListener
JFrame
public void addWindowlistener(WindowListener)
evento de ventana
MouseListener
 
public void addMouselistener(MouseListener)
eventos del ratón (clic, entrada/salida del área de un componente, pulsación de botón, suelta)
FocusListener
 
public void addFocuslistener(FocusListener)
evento «focus» (ganado, perdido)
KeyListener
 
public void addKeylistener(KeyListener)
Evento de teclado (tecla pulsada, presionada, soltada)
Componente
Método de registro de los controladores de eventos
JButton
public void addActionListener(ActionListener)
JCheckbox
public void addItemListener(ItemListener)
JCheckboxMenuItem
public void addItemListener(ItemListener)
JComboBox
public void addItemListener(ItemListener)
public void addActionListener(ActionListener)
Container
public void addContainerListener(ContainerListener)
JComponent
public void addComponentListener(ComponentListener)
public void addFocusListener(FocusListener)
public void addKeyListener(KeyListener)
public void addMouseListener(MouseListener)
public void addMouseMotionListener(MouseMotionListener)
JFrame
public void addWindowlistener(WindowListener)
JList
public void addItemListener(ItemListener)
JMenuItem
public void addActionListener(ActionListener)
JPanel
como contenedor
JScrollPane
como contenedor
JScrollBar
public void addAdjustmentListener(AdjustmentListener)
JTextComponent
public void addInputMethodListener(InputMethodListener)
public void addCaretListener(CaretListener)
JTextArea
como JTextComponent
JTextField
como JTextComponent
public void addActionListener(ActionListener)

Todos los componentes, excepto los del tipo TextXXX, al derivarse de la clase JComponent, también poseen los métodos asociados a dicha clase.

5.1.5. Los métodos de los gestores de eventos

La siguiente tabla enumera los métodos que deben implementar los distintos gestores de eventos.

Interfaz
Métodos
ActionListener
public void actionPerformed(ActionEvent)
AdjustmentListener
public void adjustmentValueChanged(AdjustmentEvent)
ComponentListener
public void componentHidden(ComponentEvent)
public void componentMoved(ComponentEvent)
public void componentResized(ComponentEvent)
public void componentShown(ComponentEvent)
ContainerListener
public void componentAdded(ContainerEvent)
public void componentRemoved(ContainerEvent)
FocusListener
public void focusGained(FocusEvent)
public void focusLost(FocusEvent)
ItemListener
public void itemStateChanged(ItemEvent)
KeyListener
public void keyPressed(KeyEvent)
public void keyReleased(KeyEvent)
public void keyTyped(KeyEvent)
MouseListener
public void mouseClicked(MouseEvent)
public void mouseEntered(MouseEvent)
public void mouseExited(MouseEvent)
public void mousePressed(MouseEvent)
public void mouseReleased(MouseEvent)
MouseMotionListener
public void mouseDragged(MouseEvent)
public void mouseMoved(MouseEvent)
TextListener
public void textValueChanged(TextEvent)
InputmethodListener
public void InputMethodTextChanged(InputMethodEvent)
public void caretPositionChanged(InputMethodEvent)
CaretLisetner
public void caretUpdate(CaretEvent)
WindowListener
public void windowActivated(WindowEvent)
public void windowClosed(WindowEvent)
public void windowClosing(WindowEvent)
public void windowDeactivated(WindowEvent)
public void windowDeiconified(WindowEvent)
public void windowIconified(WindowEvent)
public void windowOpened(WindowEvent)

5.1.6. Las clases adaptadoras

Como hemos visto en el caso de la interfaz WindowListener, existen clases denominadas XXXAdapter que implementan las interfaces XXXListener con métodos vacíos. Un gestor de eventos derivado de una clase XXXAdapter puede, por tanto, implementar solo una parte de los métodos de la interfaz XXXListener, concretamente aquellos que la aplicación necesite.

Supongamos que queremos gestionar los clics del ratón en un componente Frame f1. Podríamos asociarle un gestor de eventos mediante:

    f1.addMouseListener(new gestionnaireSouris());

y escribir:


    public class gestionnaireSouris implements MouseListener{
         // se escriben los 5 métodos de la interfaz MouseListener
        // mouseClicked, ..., mouseReleased)
    }// fin de clase

Sin embargo, como solo queremos gestionar los clics del ratón, es preferible escribir:


public class gestionnaireSouris extends MouseAdapter{
        // se escribe un único método, el que gestiona los clics del ratón
        public void mouseClicked(MouseEvent evt){

        }
}// fin de clase

La siguiente tabla muestra las clases adaptadoras de los distintos gestores de eventos:

Gestor de eventos
Adaptador
ComponentListener
ComponentAdapter
ContainerListener
ContainerAdapter
FocusListener
FocusAdapter
KeyListener
KeyAdapter
MouseListener
MouseAdapter
MouseMotionListener
MouseMotionAdapter
WindowListener
WindowAdapter

5.1.7. Conclusión

Acabamos de presentar los conceptos básicos de la creación de interfaces gráficas en Java:

  • creación de una ventana
  • creación de componentes
  • asignación de los componentes a la ventana mediante un gestor de disposición
  • asignación de gestores de eventos a los componentes

Ahora, en lugar de crear interfaces gráficas «a mano» como acabamos de hacer, vamos a utilizar JBuilder, una herramienta de desarrollo Java de Borland/Inprise en su versión 4, y supérieure.Nous utilizaremos los componentes de la biblioteca java.swing, recomendada actualmente por Sun, creador de Java.

5.2. Creación de una interfaz gráfica con JBuilder

5.2.1. Nuestro primer proyecto con JBuilder

Para familiarizarnos con JBuilder, vamos a crear una aplicación muy sencilla: una ventana vacía.

  1. Inicia JBuilder y selecciona la opción Archivo/Nuevo proyecto. A continuación, aparecerá la primera página de un asistente:

Image

  1. Rellena los siguientes campos:
Nombre del proyecto
Inicio
Esto provocará la creación de un archivo de proyecto début.jpr en la carpeta indicada en el campo «Nombre del directorio del proyecto»
Ruta raíz
Indica la carpeta en la que se creará la carpeta del proyecto que vas a crear.
Nombre del directorio del proyecto
Indica el nombre de la carpeta donde se guardarán todos los archivos del proyecto. Esta carpeta se creará en el directorio indicado en el campo «Ruta raíz»

Los archivos de origen (.java, .html, ...), los archivos de destino (.class, ...) y los archivos de copia de seguridad podrían guardarse en directorios diferentes. Si deja estos campos en blanco, se colocarán en el mismo directorio que el proyecto.

Escriba [suivant]

  1. Aparecerá una pantalla que confirmará las opciones seleccionadas en el paso anterior

Image

Seleccione [suivant]

  1. Aparecerá una nueva pantalla en la que se le pedirá que defina su proyecto:

Image

Seleccione [terminer]

  1. Comprueba que la opción Voir/projet esté marcada. Deberías ver la estructura de tu proyecto en la ventana de la izquierda.

Image

  1. Ahora vamos a crear una interfaz gráfica en este proyecto. Selecciona la opción Fichier/Nouveau/Application:

Image

En el campo classe, se introduce el nombre de la clase que se va a crear. Aquí hemos utilizado el mismo nombre que el del proyecto.

Selecciona [suivant]

  1. Aparecerá la siguiente pantalla:

Image

Clase
interfaceDébut
Es el nombre de la clase correspondiente a la ventana que se va a crear
Título
Es el texto que aparecerá en la barra de título de la ventana

Ten en cuenta que, en el ejemplo anterior, hemos indicado que la ventana se centre en la pantalla al iniciar la aplicación.

Escriba [Terminer]

  1. Ahora es cuando JBuilder resulta útil.
  • Genera los archivos fuente .java de las dos clases cuyos nombres hemos indicado: la clase de la aplicación y la de la interfaz gráfica. Estos dos archivos aparecen en la estructura del proyecto, en la ventana de la izquierda

Image

  • Para acceder al código generado para ambas clases, basta con hacer doble clic en el archivo .java correspondiente. Volveremos más adelante sobre el código generado.
  • Comprueba que la opción Voir/Structure esté marcada. Permite ver la estructura de la clase seleccionada actualmente (haciendo doble clic en el archivo .java). A continuación se muestra, por ejemplo, la estructura de la clase début:

Image

Aquí se puede ver:

  • las bibliotecas importadas (imports)

  • que hay un constructor début()

  • que hay un método estático main()

  • que hay un atributo packFrame

¿Para qué sirve tener acceso a la estructura de una clase?

  • Te ofrece una visión general de la misma. Esto resulta útil si tu clase es compleja.

  • Puedes acceder al código de un método haciendo clic sobre él en la ventana de estructura de la clase. De nuevo, esto resulta útil si tu clase tiene cientos de líneas. No tienes que revisar todas las líneas para encontrar el código que buscas.

El código generado por JBuilder ya está listo para su uso. Selecciona Ejecutar/Ejecutar el proyecto o F9 y aparecerá la ventana solicitada:

Image

Además, se cierra correctamente al hacer clic en el botón de cierre de la ventana. Acabamos de crear nuestra primera interfaz gráfica. Podemos guardar nuestro proyecto mediante la opción Archivo/Cerrar proyectos:

Image

Al pulsar Tous, se marcarán todos los proyectos presentes en la ventana anterior. OK los cerrará. Si tenemos curiosidad por ir, con el Explorador de Windows, a la carpeta de nuestro proyecto (la que se indicó al asistente al inicio de la configuración del proyecto), encontraremos allí los siguientes archivos:

Image

En nuestro ejemplo, habíamos solicitado que todos los archivos (fuente .java, destino .class, copia de seguridad .jpr~) estuvieran en la misma carpeta que el proyecto .jpr.

5.2.2. Los archivos generados por JBuilder para una interfaz gráfica

Ahora vamos a echar un vistazo a los archivos fuente .java que ha generado JBuilder. Es importante saber interpretar lo que se genera, ya que la mayoría de las veces tendremos que añadir código a lo que se ha generado. Empecemos por recuperar nuestro proyecto: Archivo/Abrir un proyecto y seleccionemos el proyecto début.jpr. Encontramos el proyecto creado anteriormente.

5.2.2.1. La clase principal

Examinemos la clase début.java haciendo doble clic en su nombre en la ventana que muestra la lista de archivos del proyecto. Tenemos el siguiente código:

import javax.swing.UIManager;
import java.awt.*;

public class début {
  boolean packFrame = false;

   /**Compilar la aplicación*/
  public début() {
    interfaceDébut frame = new interfaceDébut();

     //Validar los marcos con tamaños predefinidos
     //Ajustar los marcos con información de tamaño preferido por ejemplo, según su disposición
    if (packFrame) {
      frame.pack();
    }
    else {
      frame.validate();
    }
    //Centrar la ventana
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    Dimension frameSize = frame.getSize();
    if (frameSize.height > screenSize.height) {
      frameSize.height = screenSize.height;
    }
    if (frameSize.width > screenSize.width) {
      frameSize.width = screenSize.width;
    }
    frame.setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2);
    frame.setVisible(true);
  }
   /**Método principal*/
  public static void main(String[] args) {
    try {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    }
    catch(Exception e) {
      e.printStackTrace();
    }
    new début();
  }
}

Comentemos el código generado:

  1. la función main establece el aspecto de la ventana (setLookAndFeel) y crea una instancia de la clase début.
  2. A continuación, se ejecuta el constructor début(). Este crea una instancia frame de la clase de la ventana (new interfaceDébut()). Dicha instancia se crea, pero no se muestra.
  3. A continuación, la ventana se dimensiona según la información disponible para cada uno de sus componentes (frame.validate). Entonces comienza su existencia independiente mostrándose en pantalla y respondiendo a las acciones del usuario.
  4. La ventana se centra en la pantalla, ya que así se solicitó al configurarla con el asistente.

Veamos qué se obtendría si redujéramos el código début.java a lo estrictamente necesario, tal y como hicimos al principio del capítulo. Creemos una nueva clase. Selecciona la opción Archivo/Nueva clase:

Image

Asigna a la nueva clase el nombre début2 y cambia [Terminer]. Aparecerá un nuevo archivo en el proyecto:

Image

El archivo début2.java se reduce a su forma más simple:

public class début2 {
}

Completemos la clase de la siguiente manera:

public class début2 {
   // función principal
  public static void main(String args[]){
    // crea la ventana
    new interfaceDébut().show();    // o new interfaceDébut.setVisible(true);
  }//main
}//clase inicio2

La función main crea una instancia de la ventana interfaceDébut y la muestra (show). Antes de ejecutar nuestro proyecto, debemos señalar que la clase que contiene la función main que se va a ejecutar es ahora la clase début2. Haga clic con el botón derecho del ratón sobre el proyecto début.jpr y seleccione la opción propriétés y, a continuación, la pestaña Exécution:

21

Image

Aquí se indica que la clase principal es début (1). Pulsa el botón (2) para seleccionar otra clase principal:

Image

Seleccione début2 y cambie [OK].

Image

Seleccione [OK] para confirmar esta elección y, a continuación, solicite la ejecución del proyecto mediante Ejecutar/Ejecutar proyecto o F9. Aparecerá la misma ventana que con la clase début, salvo que no está centrada, ya que en este caso no se ha solicitado. A partir de ahora, ya no presentaremos la clase principal generada por JBuilder, ya que siempre hace lo mismo: crear una ventana. A partir de ahora nos centraremos en la clase de esta última.

5.2.2.2. La clase de la ventana

Veamos ahora el código que se ha generado para la clase interfaceDébut:

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

public class interfaceDébut extends JFrame {
  JPanel contentPane;
  BorderLayout borderLayout1 = new BorderLayout();

  /**Crear el marco*/
  public interfaceDébut() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inicializar el componente*/
  private void jbInit() throws Exception  {
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(borderLayout1);
    this.setSize(new Dimension(400, 300));
    this.setTitle("Ma première interface graphique avec Jbuilder");
  }
   /**Sustituido, para que podamos salir cuando se cierre la ventana*/
  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
    if (e.getID() == WindowEvent.WINDOW_CLOSING) {
      System.exit(0);
    }
  }
}

5.2.2.2.1. Las bibliotecas importadas
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

Se trata de las bibliotecas java.awt, java.awt.event y javax.swing. Las dos primeras eran las únicas disponibles para crear interfaces gráficas con las primeras versiones de Java. La biblioteca javax.swing es más reciente. En este caso, es necesaria para la ventana de tipo JFrame que se utiliza aquí.

5.2.2.2.2. Los atributos
  JPanel contentPane;
  BorderLayout borderLayout1 = new BorderLayout();

JPanel es un tipo de contenedor en el que se pueden colocar componentes. BorderLayout es uno de los tipos de gestores de diseño disponibles para colocar los componentes en el contenedor. En todos nuestros ejemplos, no utilizaremos ningún gestor de formato y colocaremos nosotros mismos los componentes en un lugar concreto del contenedor. Para ello, utilizaremos el gestor de formato null.

5.2.2.2.3. El generador de la ventana
   /**Crear el marco*/
  public interfaceDébut() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inicializar el componente*/
  private void jbInit() throws Exception  {
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(borderLayout1);
    this.setSize(new Dimension(400, 300));
    this.setTitle("Ma première interface graphique avec Jbuilder");
  }
  1. El desarrollador comienza diciendo que va a gestionar los eventos de la ventana (enableEvents) y, a continuación, ejecuta el método jbInit.
  2. Se obtiene (getContentPane) el contenedor (JPanel) de la ventana (JFrame)
  3. Se establece el gestor de formato (setLayout)
  4. Se establece el tamaño de la ventana (setSize)
  5. Se establece el título de la ventana (setTitle)

5.2.2.2.4. El gestor de eventos
   /**Sustituido, para que podamos salir cuando se cierre la ventana*/
  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
    if (e.getID() == WindowEvent.WINDOW_CLOSING) {
      System.exit(0);
    }

El desarrollador había indicado que la clase se encargaría de gestionar los eventos de la ventana. El método processWindowEvent es el que realiza esta tarea. Comienza por transmitir a su clase padre (JFrame) el evento WindowEvent recibido y, a continuación, si este es el evento WINDOW_CLOSING provocado al hacer clic en el botón de cierre de la ventana, la aplicación se detiene.

5.2.2.2.5. Conclusión

El código de la clase de la ventana es diferente al presentado en el ejemplo del principio del capítulo. Si utilizáramos otra herramienta de generación de código Java distinta de JBuilder, sin duda tendríamos un código aún más diferente. En la práctica, aceptaremos el código generado por JBuilder para crear la ventana, con el fin de centrarnos únicamente en escribir los controladores de eventos de la interfaz gráfica.

5.2.3. Diseñar una interfaz gráfica

5.2.3.1. Un ejemplo

En el ejemplo anterior, no habíamos incluido ningún componente en la ventana. Ahora vamos a crear una ventana con un botón, un cuadro de texto y un campo de entrada:

Image

Los campos son los siguientes:

n.º
nombre
tipo
función
1
lblSaisie
JLabel
una denominación
2
txtSaisie
JTextField
un campo de entrada
3
cmdAfficher
JButton
para mostrar en un cuadro de diálogo el contenido del campo de entrada txtSaisie

Siguiendo los mismos pasos que en el proyecto anterior, crea el proyecto interface2.jpr sin añadir por el momento ningún componente a la ventana.

Image

En la ventana anterior, selecciona la clase interface2.java de la ventana. A la derecha de esta ventana hay un archivador con pestañas:

Image

La pestaña Source da acceso al código fuente de la clase interface2.java. La pestaña Conception permite diseñar visualmente la ventana. Selecciona esta pestaña. Tienes ante ti el contenedor de la ventana, que albergará los componentes que vayas colocando en él. Por el momento está vacío. En la ventana de la izquierda se muestra la estructura de la clase:

Image

this
representa la ventana
contentPane
su contenedor, en el que se van a colocar los componentes, así como la forma en que se van a disponer dichos componentes en el contenedor (BorderLayout por defecto)
borderLayout1
una instancia del gestor de formato

Seleccione el objeto this. A continuación, aparecerá su ventana de propiedades a la derecha:

Image

Cabe destacar algunas de estas propiedades:

background
para establecer el color de fondo de la ventana
foreground
para establecer el color de los elementos gráficos de la ventana
JMenuBar
para asociar un menú a la ventana
title
para asignar un título a la ventana
resizable
para establecer el tipo de ventana
font
para establecer la fuente de los textos de la ventana

Con el objeto this aún seleccionado, se puede cambiar el tamaño del contenedor que se muestra en pantalla arrastrando los puntos de anclaje situados alrededor del contenedor:

Image

Ahora ya estamos listos para colocar componentes en el contenedor anterior. Antes de eso, vamos a cambiar el gestor de formato. Selecciona el objeto contentPane en la ventana de estructura:

Image

A continuación, en la ventana de propiedades de este objeto, selecciona la propiedad layout y elige, entre los valores posibles, el valor null:

Image

La ausencia de un gestor de formato nos permitirá colocar libremente los componentes en el contenedor. Ahora es el momento de seleccionarlos.

Cuando se selecciona el panel Conception, los componentes están disponibles en una carpeta con pestañas situada en la parte superior de la ventana de diseño:

Image

Para crear la interfaz gráfica, disponemos de componentes swing (1) y de componentes awt (2). En este caso, vamos a utilizar los componentes swing. En la barra de componentes situada arriba, selecciona un componente JLabel (3), un componente JTextField (4) y un componente JButton (5), y colócalos en el contenedor de la ventana de diseño.

Image

Ahora personalicemos cada uno de estos tres componentes:

  • la etiqueta (JLabel) jLabel1

Selecciona el componente para abrir su ventana de propiedades. En ella, modifica las siguientes propiedades: nombre: lblSaisie, texto: Introducción

  • el campo de entrada (JTextField) jTextfield1

Selecciona el componente para abrir su ventana de propiedades. En ella, modifica las siguientes propiedades: name: txtSaisie, text: no introducir nada

  • el botón (JButton): nombre: cmdAfficher, texto: Mostrar

Ahora tenemos la siguiente ventana:

Image

y la siguiente estructura:

Image

Podemos ejecutar (F9) nuestro proyecto para obtener una primera vista previa de la ventana en funcionamiento:

Image

Cierra la ventana. Ahora nos queda escribir el procedimiento asociado al clic en el botón Afficher. Selecciona el botón para acceder a su ventana de propiedades. Esta tiene dos pestañas: propriétés y événements. Seleccione événements.

Image

La columna de la izquierda de la ventana muestra los eventos posibles del botón. Al hacer clic en un botón se produce el evento actionPerformed. La columna de la derecha contiene el nombre del procedimiento que se invoca cuando se produce el evento correspondiente. Haz clic en la celda situada a la derecha del evento actionPerformed:

Image

JBuilder genera un nombre por defecto para cada gestor de eventos con el formato nomComposant_nomEvénement; en este caso, cmdAfficher_actionPerformed. Se podría borrar el nombre predeterminado y escribir otro. Para acceder al código del controlador cmdAfficher_actionPerformed, basta con hacer doble clic en su nombre, tal y como se muestra arriba. De este modo, se pasa automáticamente al panel de código fuente de la clase, situado en el esqueleto del código del controlador de eventos:

  void cmdAfficher_actionPerformed(ActionEvent e) {
 }

Ahora solo nos queda completar este código. En este caso, queremos mostrar un cuadro de diálogo con el contenido del campo txtSaisie:

  void cmdAfficher_actionPerformed(ActionEvent e) {
    JOptionPane.showMessageDialog(this, "texte saisi="+txtSaisie.getText(), 
"Vérification de la saisie",JOptionPane.INFORMATION_MESSAGE);
 }

JOptionPane es una clase de la biblioteca javax.swing. Permite mostrar mensajes acompañados de un icono o solicitar información al usuario. En este caso, utilizamos un método estático de la clase:

Image

parentComponent
el objeto contenedor «padre» del cuadro de diálogo: en este caso, this.
mensaje
un objeto que se va a mostrar. En este caso, el contenido del campo de entrada
título
el título del cuadro de diálogo
messageType
el tipo de mensaje que se va a mostrar. Determina el icono que aparecerá en el cuadro junto al mensaje. Los valores posibles son:
INFORMATION_MESSAGE, QUESTION_MESSAGE, ERROR_MESSAGE, WARNING_MESSAGE, PLAIN_MESSAGE

Ejecutemos nuestra aplicación (F9):

Image

Image

5.2.3.2. El código de la clase de la ventana

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


public class interface2 extends JFrame {
  JPanel contentPane;
  JLabel lblSaisie = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmdAfficher = new JButton();

  /**Crear el marco*/
  public interface2() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inicializar el componente*/
  private void jbInit() throws Exception  {
    lblSaisie.setText("Saisie");
    lblSaisie.setBounds(new Rectangle(25, 23, 71, 21));
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(null);
    this.setSize(new Dimension(304, 129));
    this.setTitle("Saisies & boutons - 1");
    txtSaisie.setBounds(new Rectangle(120, 21, 138, 24));
    cmdAfficher.setText("Afficher");
    cmdAfficher.setBounds(new Rectangle(111, 77, 77, 20));
    cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }
    });
    contentPane.add(lblSaisie, null);
    contentPane.add(txtSaisie, null);
    contentPane.add(cmdAfficher, null);
  }
   /**Sustituido, para que podamos salir cuando se cierre la ventana*/
  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
    if (e.getID() == WindowEvent.WINDOW_CLOSING) {
      System.exit(0);
    }
  }
  void cmdAfficher_actionPerformed(ActionEvent e) {
    JOptionPane.showMessageDialog(this, "texte saisi="+txtSaisie.getText(), "Vérification de la saisie",JOptionPane.INFORMATION_MESSAGE);
  }
}

5.2.3.2.1. Los atributos
  JPanel contentPane;
  JLabel lblSaisie = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmdAfficher = new JButton();

Encontramos el contenedor de los componentes del tipo JPanel y los tres componentes.

5.2.3.2.2. El fabricante
   /**Crear el marco*/
  public interface2() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inicializar el componente*/
  private void jbInit() throws Exception  {
    lblSaisie.setText("Saisie");
    lblSaisie.setBounds(new Rectangle(25, 23, 71, 21));
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(null);
    this.setSize(new Dimension(304, 129));
    this.setTitle("Saisies & boutons - 1");
    txtSaisie.setBounds(new Rectangle(120, 21, 138, 24));
    cmdAfficher.setText("Afficher");
    cmdAfficher.setBounds(new Rectangle(111, 77, 77, 20));
    cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }
    });
    contentPane.add(lblSaisie, null);
    contentPane.add(txtSaisie, null);
    contentPane.add(cmdAfficher, null);
  }

El constructor interface2 es similar al constructor de la interfaz gráfica anterior que hemos estudiado. Las diferencias se encuentran en el método jbInit: el código de construcción de la ventana depende de los componentes que se hayan colocado en ella. Podemos retomar el código de jbInit añadiendo nuestros propios comentarios:

  private void jbInit() throws Exception  {
       // la propia ventana (tamaño, título)
this.setSize(new Dimension(304, 129));
    this.setTitle("Saisies & boutons - 1");
         // el contenedor de los componentes
    contentPane = (JPanel) this.getContentPane();
         // no hay gestor de formato para este contenedor
    contentPane.setLayout(null);
         // la etiqueta lblSaisie (texto, posición, tamaño)
         lblSaisie.setText("Saisie");
    lblSaisie.setBounds(new Rectangle(25, 23, 71, 21));
        // el campo de entrada (posición, tamaño)
    txtSaisie.setBounds(new Rectangle(120, 21, 138, 24));
         // el botón «Mostrar» (texto, posición, tamaño)
    cmdAfficher.setText("Afficher");
    cmdAfficher.setBounds(new Rectangle(111, 77, 77, 20));
         // el gestor de eventos del botón
    cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }
    });
         // Añadir los tres componentes al contenedor
    contentPane.add(lblSaisie, null);
    contentPane.add(txtSaisie, null);
    contentPane.add(cmdAfficher, null);
  }//jbInit

Cabe destacar dos cosas:

  • este código podría haberse escrito a mano. Esto significa que se puede prescindir de JBuilder para crear una interfaz gráfica.
  • la forma en que se establece el gestor de eventos del botón cmdAfficher. El gestor de eventos del componente cmdAfficher podría haberse declarado mediante cmdAfficher.addActionListener(new gestor()) donde gestionnaire sería una clase con un método público actionPerformed encargado de gestionar el clic en el botón Afficher. En este caso, JBuilder utiliza como gestor una instancia de una clase anónima:
 new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }

Se crea una nueva instancia de la interfaz ActionListener y, a continuación, se define su método actionPerformed. Este método se limita a llamar a un método de la clase interface2. Todo esto no es más que un recurso para definir, en la misma clase que la ventana, los procedimientos de gestión de eventos de los componentes de dicha ventana. Se podría proceder de otra manera:

cmdAfficher.addActionListener(this)

lo que hace que el método actionPerformed se busque en this, es decir, en la clase de la ventana. Este segundo método parece más sencillo, pero el primero tiene una ventaja: permite tener diferentes gestores para diferentes botones, mientras que el segundo método no lo permite. En este último caso, de hecho, el único método actionPeformed debe gestionar los clics de diferentes botones y, por lo tanto, debe empezar por identificar cuál es el origen del evento antes de comenzar a funcionar.

5.2.3.2.3. Los controladores de eventos

Nos encontramos con los que ya hemos estudiado:

   /**Sustituido, para que podamos salir cuando se cierre la ventana*/
  protected void processWindowEvent(WindowEvent e) {
    super.processWindowEvent(e);
    if (e.getID() == WindowEvent.WINDOW_CLOSING) {
      System.exit(0);
    }
  }

     // clic en el botón «Mostrar»
  void cmdAfficher_actionPerformed(ActionEvent e) {
    JOptionPane.showMessageDialog(this, "texte saisi="+txtSaisie.getText(), "Vérification de la saisie",JOptionPane.INFORMATION_MESSAGE);
  }

5.2.3.3. Conclusion

A partir de los dos proyectos analizados, podemos concluir que, una vez creada la interfaz gráfica con JBuilder, el trabajo del desarrollador consiste en escribir los controladores de eventos que desea gestionar para dicha interfaz gráfica.

5.2.4. Buscar ayuda

Con Java, a menudo se necesita ayuda, sobre todo debido al gran número de clases disponibles. A continuación ofrecemos algunas indicaciones para encontrar ayuda sobre una clase. Selecciona la opción Ayuda/Temas de ayuda del menú.

Image

La pantalla de ayuda suele tener dos ventanas:

  • la de la izquierda, donde se indica lo que se busca. Tiene tres pestañas: Sommaire, Index y Chercher.
  • la de la derecha, que muestra el resultado de la búsqueda

Hay disponible una guía sobre cómo utilizar la ayuda de JBuilder. Selecciona en la ayuda de JBuilder la opción Ayuda/Uso de la ayuda. Allí se explica cómo utilizar la ayuda. Por ejemplo, se indican los diferentes componentes del visor de ayuda:

Image

Examinemos con más detalle las páginas Sommaire y Index.

5.2.4.1. Ayuda: Índice

Image

5.2.4.1.1. Índice: Introducción a Java

Aquí se pueden encontrar los conceptos básicos de Java, pero no solo eso, tal y como muestra la lista de temas tratados en esta opción:

Image

5.2.4.1.2. Índice: Tutoriales

Si seleccionamos la opción «Tutoriales» en el índice anterior, la ventana de la derecha nos muestra una lista de los tutoriales disponibles:

Image

Los tutoriales básicos resultan especialmente interesantes para empezar a familiarizarse con JBuilder. Existen muchos otros además de los presentados anteriormente y, cuando se quiera desarrollar una aplicación, puede resultar útil comprobar antes si hay algún tutorial que pueda ayudarnos.

5.2.4.1.3. Índice: el JDK

Al seleccionar la opción Java 2 JDK 1.3, se tiene acceso a todas las bibliotecas de JDK. Por lo general, no es aquí donde hay que buscar si necesitamos información sobre una clase concreta y no sabemos en qué biblioteca se encuentra. Sin embargo, esta opción resulta útil si nos interesa tener una visión general de las bibliotecas de Java.

5.2.4.2. Ayuda: Índice

Selecciona la pestaña Índice de la ventana de la izquierda en la ayuda. Esta opción te permite, por ejemplo, encontrar ayuda sobre una clase. Supongamos, por ejemplo, que quieres conocer los métodos de los campos de entrada swing y JTextField. Escriba JTextField en el campo de búsqueda:

Image

La ayuda mostrará las entradas del índice que comienzan por el texto introducido:

Image

Ahora solo tienes que hacer doble clic en la entrada que te interese, en este caso JTextField class. La ayuda sobre esta clase se mostrará entonces en la ventana de la derecha:

Image

A continuación, se te ofrece una descripción completa de la clase.

5.2.5. Algunos componentes de Swing

A continuación, presentamos diversas aplicaciones en las que se utilizan los componentes swing más habituales, con el fin de descubrir sus principales métodos y propiedades. Para cada aplicación, mostramos la interfaz gráfica y el código relevante, en particular el de los gestores de eventos.

5.2.5.1. Componentes JLabel y JTextField

Ya hemos visto estos dos componentes. JLabel es un componente de texto y JTextField, un componente de campo de entrada. Sus dos métodos principales son

String getText()
para obtener el contenido del campo de entrada o el texto de la etiqueta
void setText(String unTexte)
para introducir unTexte en el campo o en la etiqueta

Los eventos que se suelen utilizar para JTextField son los siguientes:

actionPerformed
indica que el usuario ha validado (tecla Intro) el texto introducido
caretUpdate
indica que el usuario ha movido el cursor de introducción
inputMethodChanged
indica que el usuario ha modificado el campo de entrada

A continuación se muestra un ejemplo que utiliza el evento caretUpdate para realizar un seguimiento de los cambios en un campo de entrada:

Image

n.º
tipo
nombre
función
1
JTextField
txtSaisie
campo de entrada
2
JTextField
txtControle
muestra el texto de 1 en tiempo real
3
JButton
cmdEffacer
para borrar los campos 1 y 2
4
JButton
cmdQuitter
para salir de la aplicación

El código correspondiente a esta aplicación es el siguiente:

import java.awt.*;
....


public class Cadre1 extends JFrame {
  JPanel contentPane;
  JTextField txtSaisie = new JTextField();
  JLabel jLabel1 = new JLabel();
  JLabel jLabel2 = new JLabel();
  JTextField txtControle = new JTextField();
  JButton CmdEffacer = new JButton();
  JButton CmdQuitter = new JButton();

  /**Crear el marco*/
  public Cadre1() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inicializar el componente*/
  private void jbInit() throws Exception  {
    ....
    txtSaisie.addCaretListener(new javax.swing.event.CaretListener() {
      public void caretUpdate(CaretEvent e) {
        txtSaisie_caretUpdate(e);
      }
    });

...
    CmdEffacer.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        CmdEffacer_actionPerformed(e);
      }
    });
...
    CmdQuitter.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        CmdQuitter_actionPerformed(e);
      }
    });
....
}

   /**Sustituido, para que podamos salir cuando se cierre la ventana*/
  protected void processWindowEvent(WindowEvent e) {
...
  }

  void txtSaisie_caretUpdate(CaretEvent e) {
         // el cursor de entrada se ha movido
    txtControle.setText(txtSaisie.getText());
  }

  void CmdQuitter_actionPerformed(ActionEvent e) {
        // Salimos de la aplicación
    System.exit(0);
  }

  void CmdEffacer_actionPerformed(ActionEvent e) {
        // Se borra el contenido del campo de entrada
    txtSaisie.setText("");
  }
}

A continuación se muestra un ejemplo de ejecución:

Image

5.2.5.2. componente JComboBox

Image

Image

Un componente JComboBox es una lista desplegable acompañada de un campo de entrada: el usuario puede elegir un elemento en (2) o escribir texto en (1). Por defecto, los JComboBox no son editables. Para que sean editables, hay que llamar explícitamente al método setEditable(true). Para obtener más información sobre la clase JComboBox, escribe JComboBox en el índice de la ayuda.

El objeto JComboBox se puede crear de diferentes maneras:

new JComboBox()
crea un cuadro combinado vacío
new JComboBox (Object[] items)
crea un cuadro combinado que contiene una matriz de objetos
new JComboBox(Vector items)
lo mismo con un vector de objetos

Puede sorprender que un combo pueda contener objetos, cuando normalmente contiene cadenas de caracteres. A nivel visual, así será. Si un JComboBox contiene un objeto obj, muestra la cadena obj.toString(). Recordemos que todo objeto tiene un método toString heredado de la clase Object, que devuelve una cadena de caracteres «representativa» del objeto.

Los métodos útiles de la clase JCombobox son los siguientes:

void addItem(Object unObjet)
añade un objeto al cuadro combinado
int getItemCount()
devuelve el número de elementos del cuadro combinado
Object getItemAt(int i)
devuelve el objeto n.º i del menú desplegable
void insertItemAt(Object unObjet, int i)
inserta unObjet en la posición i del combo
int getSelectedIndex()
devuelve el número del elemento seleccionado en el menú desplegable
Object getSelectedItem()
devuelve el objeto seleccionado en el menú desplegable
void setSelectedIndex(int i)
selecciona el elemento i del menú desplegable
void setSelectedItem(Object unObjet)
selecciona el objeto especificado en el menú desplegable
void removeAllItems()
vacía el menú desplegable
void removeItemAt(int i)
elimina el elemento n.º i del cuadro combinado
void removeItem(Object unObjet)
elimina el objeto especificado del cuadro combinado
void setEditable(boolean val)
hace que el cuadro combinado sea editable (val=true) o no (val=false)

Al seleccionar un elemento de la lista desplegable se produce el evento actionPerformed, que puede utilizarse para recibir una notificación del cambio de selección en el cuadro combinado. En la siguiente aplicación, utilizamos este evento para mostrar el elemento que se ha seleccionado en la lista.

Image

Solo mostramos el código relevante de la ventana.

public class Cadre1 extends JFrame {
  JPanel contentPane;
  JComboBox jComboBox1 = new JComboBox();
  JLabel jLabel1 = new JLabel();

   /**Crear el cuadro*/
  public Cadre1() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // procesamiento: se rellena el menú desplegable
    String[] infos={"un","deux","trois","quatre"};
    for(int i=0;i<infos.length;i++)
      jComboBox1.addItem(infos[i]);
  }
   /**Inicializar el componente*/
  private void jbInit() throws Exception  {
        ....

    jComboBox1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        jComboBox1_actionPerformed(e);
      }
    });
       .... 
  }

  void jComboBox1_actionPerformed(ActionEvent e) {
        // Se ha seleccionado un nuevo elemento: se muestra
    JOptionPane.showMessageDialog(this,jComboBox1.getSelectedItem(),
"actionPerformed",JOptionPane.INFORMATION_MESSAGE);
  }

}

5.2.5.3. Componente JList

El componente Swing JList es más complejo que su homólogo de la biblioteca awt. Hay dos diferencias importantes:

  • el contenido de la lista lo gestiona un objeto distinto de la propia lista. En este caso utilizaremos un objeto DefaultListModel, que se emplea como un Vector, pero que, además, avisa al objeto JList en cuanto cambia su contenido, para que la visualización de la lista también cambie.
  • La lista no se desplaza de forma predeterminada. Es necesario colocar la lista en un contenedor ScrollPane, que sí permite dicho desplazamiento.

En el código fuente, la definición de una lista se puede realizar de la siguiente manera:

      // el vector de valores de la lista 
DefaultlistModel valeurs=new DefaultListModel();
// la propia lista a la que se asocia el vector de sus valores
  JList jList1 = new JList(valeurs);
     // el contenedor desplazable en el que se coloca la lista para obtener una lista desplazable
  JScrollPane jScrollPane1 = new JScrollPane(jList1);

Para incluir la lista jList1 en el contenedor jScrollPane1, el código generado por JBuilder procede de forma diferente:

  • declaración del contenedor en los atributos de la ventana
  JScrollPane jScrollPane1 = new JScrollPane();
  • y, a continuación, en el código de jbInit, la lista se asocia al contenedor
    jScrollPane1.getViewport().add(jList1, null);

Para añadir valores a la lista JList1 anterior, basta con añadirlos a su vector de valores valeurs:

    // inicializar lista
    for(int i=0;i<10;i++)
      valeurs.addElement(""+i);

y se obtiene entonces la siguiente ventana:

Image

¿Cómo se construye esta interfaz?

  • Se selecciona un componente JScrollPane en la página «Contenedores Swing» de los componentes y se arrastra a la ventana, ajustando su tamaño al deseado
  • Se selecciona un componente JList en la página «Swing» de los componentes y se coloca en el contenedor JScrollPane, donde ocupa todo el espacio.

El código generado por JBuilder debe modificarse ligeramente para obtener el siguiente código:

public class interfaceAppli extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  DefaultListModel valeurs=new DefaultListModel();
  JList jList1 = new JList(valeurs);
  JScrollPane jScrollPane1 = new JScrollPane();
  JLabel jLabel2 = new JLabel();


   /**Crear el marco*/
  public interfaceAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // procesamiento
     // se incluye la lista en el scrollPane
    // inicialización de la lista
    for(int i=0;i<10;i++)
      valeurs.addElement(""+i);
  }

   /**Inicializar el componente*/
  private void jbInit() throws Exception  {
        ....
         // la lista jList1 está asociada al contenedor jcrollPane1
    jScrollPane1.getViewport().add(jList1, null);
  }
   /**Sustituido, así podemos salir cuando se cierre la ventana*/
  protected void processWindowEvent(WindowEvent e) {
        ....
  }
}

Veamos ahora los métodos principales de la clase JList buscando JList en el índice de la ayuda. El objeto JList se puede crear de diferentes maneras:

Image

Un método sencillo es el que hemos utilizado: crear un DefaultListModel V vacío y, a continuación, asociarlo a la lista que se va a crear mediante new JList(V). El contenido de la lista no lo gestiona el objeto JList, sino el objeto que contiene los valores de la lista. Si el contenido se ha creado mediante un objeto DefaultListModel basado en la clase Vector, se podrán utilizar los métodos de la clase Vector para añadir, insertar y eliminar elementos de la lista. Una lista puede ser de selección simple o múltiple. Esto se establece mediante el método setSelectionMode:

Image

Se puede conocer el modo de selección actual con getSelectionMode:

Image

El elemento o los elementos seleccionados se pueden obtener mediante los siguientes métodos:

Image

Sabemos cómo asociar un vector de valores a una lista con el constructor JList(DefaultListModel). A la inversa, podemos obtener el objeto DefaultListModel a partir de una lista JList mediante:

Image

Ya sabemos lo suficiente para escribir la siguiente aplicación:

Image

Los componentes de esta ventana son los siguientes:

n.º
tipo
nombre
función
1
JTextField
txtSaisie
campo de entrada
2
JList
jList1
lista contenida en un contenedor jScrollPane1
3
JList
jList2
lista contenida en un contenedor jScrollPane2
4
JButton
cmd1To2
transfiere los elementos seleccionados de la lista 1 a la lista 2
5
JButton
cmd2To1
hace lo contrario
6
JButton
cmdRaz1
vacía la lista 1
7
JButton
cmdRaz2
vacía la lista 2

El usuario escribe texto en el campo (1) y lo valida. A continuación, se produce el evento actionPerformed en el campo de entrada, que se utiliza para añadir el texto introducido a la lista 1. Este es el código necesario para esta primera función:

public class interfaceAppli extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JLabel jLabel2 = new JLabel();
  JLabel jLabel3 = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmd1To2 = new JButton();
  JButton cmd2To1 = new JButton();
  DefaultListModel v1=new DefaultListModel();
  DefaultListModel v2=new DefaultListModel();
  JList jList1 = new JList(v1);
  JList jList2 = new JList(v2);
  JScrollPane jScrollPane1 = new JScrollPane();
  JScrollPane jScrollPane2 = new JScrollPane();
  JButton cmdRaz1 = new JButton();
  JButton cmdRaz2 = new JButton();
  JLabel jLabel4 = new JLabel();

   /**Crear el marco*/
  public interfaceAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }//interfaceAppli

   /**Inicializar el componente*/
  private void jbInit() throws Exception  {
    ...
    txtSaisie.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtSaisie_actionPerformed(e);
      }
    });
        ...
         // Jlist1 se coloca en el contenedor jScrollPane1
    jScrollPane1.getViewport().add(jList1, null);
        // Jlist2 se coloca en el contenedor jScrollPane2
    jScrollPane2.getViewport().add(jList2, null);
        ...
  }
   /**Sustituido, para que podamos salir cuando se cierre la ventana*/
  protected void processWindowEvent(WindowEvent e) {
...
  }

  void txtSaisie_actionPerformed(ActionEvent e) {
     // El texto introducido se ha validado
     // se recupera sin los espacios iniciales y finales
    String texte=txtSaisie.getText().trim();
     // si está vacío, no lo queremos
    if(texte.equals("")){
      // mensaje de error
      JOptionPane.showMessageDialog(this,"Vous devez taper un texte",
        "Erreur",JOptionPane.WARNING_MESSAGE);
      // fin
      return;
    }//si
    // si no está vacío, se añade a los valores de la lista 1
    v1.addElement(texte);
     // y se vacía el campo de entrada
    txtSaisie.setText("");
  }/// txtSaisie_actionperformed
}//clase

El código para transferir los elementos seleccionados de una lista a otra es el siguiente:

  void cmd1To2_actionPerformed(ActionEvent e) {
    // transferencia de los elementos seleccionados de la lista 1 a la lista 2
    transfert(jList1,jList2);
  }//cmd1To2

  void cmd2To1_actionPerformed(ActionEvent e) {
    // transferencia de los elementos seleccionados en jList2 a jList1
      transfert(jList2,jList1);
  }//cmd2TO1

  private void transfert(JList L1, JList L2){
      // transferencia de los elementos seleccionados de la lista 1 a la lista 2
       // se recupera la matriz de índices de los elementos seleccionados en L1
      int[] indices=L1.getSelectedIndices();
      // ¿Hay que hacer algo?
      if (indices.length==0) return;
      // se recuperan los valores de L1
      DefaultListModel v1=(DefaultListModel)L1.getModel();
      // y los de L2
      DefaultListModel v2=(DefaultListModel)L2.getModel();
      for(int i=indices.length-1;i>=0;i--){
        // se añaden a L2 los valores seleccionados en L1
        v2.addElement(v1.elementAt(indices[i]));
        // los elementos de L1 copiados en L2 deben eliminarse de L1
        v1.removeElementAt(indices[i]);
      }//para
  }//transferencia

El código asociado a los botones Raz es de lo más sencillo:

  void cmdRaz1_actionPerformed(ActionEvent e) {
    // vaciar la lista 1
    v1.removeAllElements();
  }//comando Raz1

  void cmdRaz2_actionPerformed(ActionEvent e) {
    // vaciar lista 2
    v2.removeAllElements();
  }///cmd Raz2

5.2.5.4. Casillas de selección JCheckBox, botones de opción JButtonRadio

Nos proponemos escribir la siguiente aplicación:

Image

Los componentes de la ventana son los siguientes:

n.º
tipo
nombre
función
1
JButtonRadio
jButtonRadio1
jButtonRadio2
jButtonRadio3
3 botones de opción que forman parte del grupo buttonGroup1
2
JCheckBox
jCheckBox1
jCheckBox2
jCheckBox3
3 casillas de selección
3
JList
jList1
una lista dentro de un contenedor jScrollPane1
4
ButtonGroup
buttonGroup1
componente no visible: sirve para agrupar los tres botones de opción, de modo que, cuando uno de ellos se active, los demás se desactiven.

Un grupo de botones de opción se puede crear de la siguiente manera:

  • se colocan cada uno de los botones de opción sin preocuparse por agruparlos
  • se coloca en el contenedor un componente Swing ButtonGroup. Este componente no es visible. Por lo tanto, no aparece en el diseñador de la ventana. Sin embargo, sí aparece en su estructura:

Image

Arriba, en la rama Autre, se pueden ver los atributos no visuales de la ventana. Una vez creado un grupo de botones de opción, se puede asociar a él cada uno de los botones de opción. Para ello, se seleccionan las propiedades del botón de opción:

Image

y, en la propiedad buttonGroup del botón de opción, se introduce el nombre del grupo en el que se desea incluir el botón de opción, en este caso buttonGroup1. Se repite esta operación para los tres botones de opción.

El método principal de los botones de radio y las casillas de selección es el método isSelected(), que indica si la casilla o el botón está marcado. El texto asociado al componente se puede obtener con getText() y establecer con setText(String unTexte). La casilla o el botón de opción se puede marcar con el método setSelected(boolean value).

Al hacer clic en un botón de radio o en una casilla de selección, se activa el evento actionPerformed. En el código siguiente, utilizamos este evento para realizar un seguimiento de los cambios en los valores de los botones de radio y las casillas de selección:

public class interfaceAppli extends JFrame {
  JPanel contentPane;
  JRadioButton jRadioButton1 = new JRadioButton();
  JRadioButton jRadioButton2 = new JRadioButton();
  JRadioButton jRadioButton3 = new JRadioButton();
  JCheckBox jCheckBox1 = new JCheckBox();
  JCheckBox jCheckBox2 = new JCheckBox();
  JCheckBox jCheckBox3 = new JCheckBox();
  ButtonGroup buttonGroup1 = new ButtonGroup();
  JScrollPane jScrollPane1 = new JScrollPane();
  DefaultListModel valeurs=new DefaultListModel();
  JList jList1 = new JList(valeurs);

   /**Crear el marco*/
  public interfaceAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inicializar el componente*/
  private void jbInit() throws Exception  {
    jRadioButton1.setSelected(true);
    jRadioButton1.setText("un");
    jRadioButton1.setBounds(new Rectangle(57, 31, 49, 23));
    jRadioButton1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheRadioButtons(e);
      }
    });

    jRadioButton2.setBounds(new Rectangle(113, 30, 49, 23));
    jRadioButton2.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheRadioButtons(e);
      }
    });
    jRadioButton2.setText("deux");

    jRadioButton3.setBounds(new Rectangle(168, 30, 49, 23));
    jRadioButton3.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheRadioButtons(e);
      }
    });
    jRadioButton3.setText("trois");

// los botones de opción están agrupados
    buttonGroup1.add(jRadioButton1);
    buttonGroup1.add(jRadioButton2);
    buttonGroup1.add(jRadioButton3);

// casillas de selección
    jCheckBox1.setText("A");
    jCheckBox1.setBounds(new Rectangle(58, 69, 32, 17));
    jCheckBox1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheCases(e);
      }
    });

    jCheckBox2.setBounds(new Rectangle(112, 69, 40, 17));
    jCheckBox2.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheCases(e);
      }
    });
    jCheckBox2.setText("B");

    jCheckBox3.setText("C");
    jCheckBox3.setBounds(new Rectangle(170, 69, 37, 17));
    jCheckBox3.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        afficheCases(e);
      }
    });

    ....
  }
   /**Sustituido, para que podamos salir cuando se cierre la ventana*/
  protected void processWindowEvent(WindowEvent e) {
        ...
  }

  private void afficheRadioButtons(ActionEvent e){
     // muestra los valores de los 3 botones de opción
    valeurs.addElement("boutons radio=("+jRadioButton1.isSelected()+","+
      jRadioButton2.isSelected()+","+jRadioButton3.isSelected()+")");
  }//afficheRadioButtons

  void afficheCases(ActionEvent e) {
     // muestra los valores de las casillas de selección
    valeurs.addElement("cases à cocher=["+jCheckBox1.isSelected()+","+
      jCheckBox2.isSelected()+","+jCheckBox3.isSelected()+")");
  }//afficheCases

}//clasifica

A continuación se muestra un ejemplo de ejecución:

Image

5.2.5.5. componente JScrollBar

Creemos la siguiente aplicación:

Image

n.º
tipo
nombre
función
1
JScrollBar
jScrollBar1
un variador horizontal
2
JScrollBar
jScrollBar2
un variador vertical
3
JTextField
txtvaleurHS
muestra el valor del variador horizontal 1; también permite fijar dicho valor
4
JTextField
txtvaleurVS
muestra el valor del regulador vertical 2; también permite fijar dicho valor
  • Un regulador JScrollBar permite al usuario seleccionar un valor dentro de un rango de valores enteros, representado por la «banda» del regulador sobre la que se desplaza un cursor.
  • En un regulador horizontal, el extremo izquierdo representa el valor mínimo del rango, el extremo derecho el valor máximo y el cursor el valor actual seleccionado. En un regulador vertical, el mínimo está representado por el extremo superior y el máximo por el extremo inferior. El par (mín., máx.) tiene por defecto el valor (0,100).
  • Al hacer clic en los extremos del regulador, el valor varía en un incremento (positivo o negativo) según el extremo en el que se haya hecho clic, denominado unitIncrement, cuyo valor por defecto es 1.
  • Al hacer clic a ambos lados del cursor, el valor varía en un incremento (positivo o negativo) según el extremo en el que se haya hecho clic, denominado blockIncrement, cuyo valor por defecto es 10.
  • Estos cinco valores (mín., máx., valor, unitIncrement, blockIncrement) pueden obtenerse mediante los métodos getMinimum(), getMaximum(), getValue(), getUnitIncrement() y getBlockIncrement(), todos los cuales devuelven un entero y pueden establecerse mediante los métodos setMinimum(int min), setMaximum(int max), setValue(int val), setUnitIncrement(int uInc), setBlockIncrement(int bInc)

Hay algunos aspectos que conviene conocer sobre el uso de los componentes JScrollBar. En primer lugar, se encuentra en la barra de componentes de Swing:

Image

Al colocarlo en el contenedor, su orientación predeterminada es vertical. Se puede cambiar a horizontal mediante la propiedad orientation que se indica a continuación:

Image

En la hoja de propiedades anterior se puede ver que tenemos acceso a las propiedades minimum, maximum, value, unitIncrement, blockIncrement y JScrollbar. Por lo tanto, se pueden establecer durante el diseño. Cuando se coloca una barra de desplazamiento en el contenedor, su «rango de variación» no aparece:

Image

Este problema se puede solucionar añadiendo un borde al componente. Esto se hace mediante su propiedad border, que puede adoptar diferentes valores:

Image

Este es, por ejemplo, el resultado de RaisedBevel:

Image

Al hacer clic en el extremo superior de un control deslizante vertical, su valor disminuye. Esto puede sorprender al usuario medio, que normalmente espera que el valor «aumente». Este problema se soluciona asignando un valor negativo a unitIncrement y blockIncrement.

¿Cómo se puede seguir la evolución de un control deslizante? Cuando cambia su valor, se produce el evento adjustmentValueChanged. Basta con asociar un procedimiento a este evento para recibir una notificación cada vez que varíe el valor de la barra de desplazamiento.

Image

El código útil de nuestra aplicación es el siguiente:

....

public class cadreAppli extends JFrame {
  JPanel contentPane;
  JScrollBar jScrollBar1 = new JScrollBar();
  Border border1;
  JTextField txtValeurHS = new JTextField();
  JScrollBar jScrollBar2 = new JScrollBar();
  JTextField txtValeurVS = new JTextField();
  TitledBorder titledBorder1;

   /**Crear el marco*/
  public cadreAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inicializar el componente*/
  private void jbInit() throws Exception  {
...

// un borde para las barras de desplazamiento
    border1 = BorderFactory.createBevelBorder(BevelBorder.RAISED,Color.white,Color.white,new Color(134, 134, 134),new Color(93, 93, 93));
        // sin título en el borde
    titledBorder1 = new TitledBorder("");

    jScrollBar1.setOrientation(JScrollBar.HORIZONTAL);
    jScrollBar1.setBorder(BorderFactory.createRaisedBevelBorder());
    jScrollBar1.setAutoscrolls(true);
    jScrollBar1.setBounds(new Rectangle(37, 17, 218, 20));
    jScrollBar1.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        jScrollBar1_adjustmentValueChanged(e);
      }
    });

    txtValeurHS.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtValeurHS_actionPerformed(e);
      }
    });

    jScrollBar2.setBounds(new Rectangle(39, 51, 30, 27));
    jScrollBar2.addAdjustmentListener(new java.awt.event.AdjustmentListener() {
      public void adjustmentValueChanged(AdjustmentEvent e) {
        jScrollBar2_adjustmentValueChanged(e);
      }
    });
    jScrollBar2.setAutoscrolls(true);
    jScrollBar2.setUnitIncrement(-1);
    jScrollBar2.setBorder(BorderFactory.createRaisedBevelBorder());

    txtValeurVS.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtValeurVS_actionPerformed(e);
      }
    });

    ......
  }

   /**Sustituido, para que podamos salir cuando se cierre la ventana*/
  protected void processWindowEvent(WindowEvent e) {
...
  }

  void jScrollBar1_adjustmentValueChanged(AdjustmentEvent e) {
    // el valor de la barra de desplazamiento 1 ha cambiado
    txtValeurHS.setText(""+jScrollBar1.getValue());
  }

  void jScrollBar2_adjustmentValueChanged(AdjustmentEvent e) {
    // el valor de la barra de desplazamiento 2 ha cambiado
    txtValeurVS.setText(""+jScrollBar2.getValue());
  }

  void txtValeurHS_actionPerformed(ActionEvent e) {
    // se fija el valor de la barra de desplazamiento horizontal
    setValeur(jScrollBar1,txtValeurHS);
  }

  void txtValeurVS_actionPerformed(ActionEvent e) {
    // se fija el valor de la barra de desplazamiento vertical
    setValeur(jScrollBar2,txtValeurVS);
  }

  private void setValeur(JScrollBar jS, JTextField jT){
     // se fija el valor de la barra de desplazamiento jS con el texto del campo jT
    int valeur=0;
    try{
      valeur=Integer.parseInt(jT.getText());
      jS.setValue(valeur);
    }
    catch (Exception e){
      // se muestra el error
      afficher(""+e);
    }//try-catch
  }//setValeur

  void afficher(String message){
    // muestra un mensaje en un cuadro
    JOptionPane.showMessageDialog(this,message,"Menus",JOptionPane.INFORMATION_MESSAGE);
  }//mostrar

}

A continuación se muestra un ejemplo de ejecución:

Image

5.2.5.6. Componente JTextArea

El componente JTextArea es un componente en el que se pueden introducir varias líneas de texto, a diferencia del componente JTextField, en el que solo se puede introducir una línea. Si este componente se coloca en un contenedor desplazable (JScrollPane), se obtiene un campo de entrada de texto desplazable. Este tipo de componente podría encontrarse, por ejemplo, en una aplicación de correo electrónico en la que el texto del mensaje que se va a enviar se escribiría en un componente JTextArea. Los métodos habituales son String getText() para conocer el contenido del campo de texto, setText(String unTexte) para introducir unTexte en el campo de texto, y append(String unTexte) para añadir unTexte al texto ya presente en el campo de texto. Consideremos la siguiente aplicación:

Image

n.º
tipo
nombre
función
1
JTextArea
txtTexte
un campo de texto multilínea
2
JButton
cmdAfficher
muestra el contenido de 1 en un cuadro de diálogo
3
JButton
cmdEffacer
borra el contenido de 1
4
JTextField
txtAjout
texto añadido al texto de 1 al pulsar la tecla Intro.
5
JScrollPane
jScrollPane1
Contenedor con desplazamiento en el que se ha colocado el cuadro de texto 1 para que sea un cuadro de texto con desplazamiento.

El código correspondiente es el siguiente:

.....

public class cadreAppli extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JButton cmdAfficher = new JButton();
  JScrollPane jScrollPane1 = new JScrollPane();
  JTextArea txtTexte = new JTextArea();
  JLabel jLabel2 = new JLabel();
  JTextField txtAjout = new JTextField();
  JButton cmdEffacer = new JButton();

  /**Crear el marco*/
  public cadreAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inicializar el componente*/
  private void jbInit() throws Exception  {

    cmdAfficher.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdAfficher_actionPerformed(e);
      }
    });
    txtAjout.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtAjout_actionPerformed(e);
      }
    });
    cmdEffacer.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdEffacer_actionPerformed(e);
      }
    });
    .......
    jScrollPane1.getViewport().add(txtTexte, null);
  }
   /**Sustituido, para que podamos salir cuando se cierre la ventana*/
  protected void processWindowEvent(WindowEvent e) {
        ........
  }

  void cmdAfficher_actionPerformed(ActionEvent e) {
    // muestra el contenido de TextArea
    afficher(txtTexte.getText());
  }

    void afficher(String message){
     // muestra un mensaje en un cuadro
    JOptionPane.showMessageDialog(this,message,"Suivi",JOptionPane.INFORMATION_MESSAGE);
  }// mostrar

  void cmdEffacer_actionPerformed(ActionEvent e) {
    txtTexte.setText("");
  }//cmdEffacer_actionPerformed

  void txtAjout_actionPerformed(ActionEvent e) {
    // añadir texto
    txtTexte.append(txtAjout.getText());
     // borrar y añadir
    txtAjout.setText("");
  }//
}

5.2.6. Eventos del ratón

Cuando se dibuja en un contenedor, es importante conocer la posición del ratón para, por ejemplo, mostrar un punto al hacer clic. Los movimientos del ratón provocan eventos en el contenedor por el que se desplaza. A continuación se muestran, por ejemplo, los que ofrece JBuilder para un contenedor JPanel:

Image

mouseClicked
clic del ratón
mouseDragged
el ratón se desplaza, botón izquierdo pulsado
mouseEntered
el ratón acaba de entrar en el área del contenedor
mouseExited
el ratón acaba de salir de la superficie del contenedor
mouseMoved
el ratón se mueve
mousePressed
Se pulsa el botón izquierdo del ratón
mouseReleased
Se suelta el botón izquierdo del ratón

Aquí tienes un programa que te ayudará a comprender mejor en qué momentos se producen los distintos eventos del ratón:

Image

n.º
tipo
nombre
función
1
JTextField
txtPosition
para mostrar la posición del ratón en el contenedor (si procede, MouseMoved)
2
JList
lstAffichage
para mostrar los eventos del ratón distintos de MouseMoved
3
JButton
cmdEffacer
para borrar el contenido de 2

Al ejecutar este programa, esto es lo que se obtiene al hacer clic:

Image

Los eventos se apilan de arriba abajo en la lista. Por lo tanto, la captura de pantalla anterior indica que un clic provoca tres eventos, en este orden:

  1. MousePressed al pulsar el botón
  2. MouseReleased al soltarlo
  3. MouseClicked, que indica que la sucesión de los dos eventos anteriores se considera un clic. Podría tratarse de un doble clic. Pero, en el ejemplo anterior, la información clickCount=1 indica que se trata de un clic simple.

Ahora, si pulsamos el botón, movemos el ratón y soltamos el botón:

Image

Aquí vemos los tres eventos:

  1. MousePressed al pulsar inicialmente el botón
  2. MouseDragged al mover el ratón con el botón pulsado
  3. MouseReleased al soltar el botón

En los dos ejemplos anteriores, se observa que un evento del ratón trae consigo diversa información, entre la que se incluyen las coordenadas (x, y) del ratón, por ejemplo (408,65) en la primera línea anterior.

Si seguimos así, descubrimos que el evento MouseExited se activa en cuanto el ratón sale del contenedor o pasa por encima de uno de sus componentes. En este último caso, el contenedor recibe el evento MouseExited y el componente, el evento MouseEntered. Ocurrirá lo contrario cuando el ratón salga del componente para volver al contenedor.

¿Qué ocurre al hacer doble clic?

Image

Se producen exactamente los mismos eventos que con un clic simple. La única diferencia es que el evento incluye la información clickCount=2 (véase más arriba), lo que indica que, de hecho, se ha producido un doble clic.

El código relevante de esta aplicación es el siguiente:

public class Cadre1 extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JTextField txtPosition = new JTextField();
  JScrollPane jScrollPane1 = new JScrollPane();
  DefaultListModel valeurs=new DefaultListModel();
  JList lstAffichage = new JList(valeurs);
  JButton cmdEffacer = new JButton();

  /**Crear el marco*/
  public Cadre1() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inicializar el componente*/
  private void jbInit() throws Exception  {

    contentPane.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
      public void mouseMoved(MouseEvent e) {
        contentPane_mouseMoved(e);
      }
      public void mouseDragged(MouseEvent e) {
        contentPane_mouseDragged(e);
      }
    });

    contentPane.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mouseEntered(MouseEvent e) {
        contentPane_mouseEntered(e);
      }
      public void mouseExited(MouseEvent e) {
        contentPane_mouseExited(e);
      }
      public void mousePressed(MouseEvent e) {
        contentPane_mousePressed(e);
      }
      public void mouseClicked(MouseEvent e) {
        contentPane_mouseClicked(e);
      }
      public void mouseReleased(MouseEvent e) {
        contentPane_mouseReleased(e);
      }
    });

    cmdEffacer.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        cmdEffacer_actionPerformed(e);
      }
    });

    jScrollPane1.getViewport().add(lstAffichage, null);

...............
  }
   /**Sustituido, para que podamos salir cuando se cierre la ventana*/
  protected void processWindowEvent(WindowEvent e) {
    .........
  }

  void contentPane_mouseMoved(MouseEvent e) {
    txtPosition.setText("("+e.getX()+","+e.getY()+")");
  }

  void contentPane_mouseDragged(MouseEvent e) {
    afficher(e);
  }

  void contentPane_mouseEntered(MouseEvent e) {
    afficher(e);
  }

  void afficher(MouseEvent e){
     // muestra el evento e en la lista lstAffichage
    valeurs.insertElementAt(e,0);
  }

  void contentPane_mouseExited(MouseEvent e) {
    afficher(e);
  }

  void contentPane_mousePressed(MouseEvent e) {
    afficher(e);
  }

  void contentPane_mouseClicked(MouseEvent e) {
    afficher(e);
  }

  void contentPane_mouseReleased(MouseEvent e) {
    afficher(e);
  }

  void cmdEffacer_actionPerformed(ActionEvent e) {
     // borra la lista
    valeurs.removeAllElements();
  }
}

5.2.7. Crear una ventana con menú

Veamos ahora cómo crear una ventana con menú con JBuilder. Vamos a crear la siguiente ventana:

Image

Image

Crea un nuevo proyecto que, inicialmente, contenga una ventana vacía. Selecciona en la lista de componentes «Contenedores Swing» el componente JMenuBar (véase la figura 1 más abajo) y arrástralo hasta la ventana que estás diseñando.

Image

No aparece nada en la ventana de diseño, pero el componente JMenuBar aparece en el panel de estructura de la ventana:

Image

Haz doble clic en el elemento jMenuBar1 anterior para acceder al menú en modo de diseño:

Image

1
Insertar un elemento de menú
2
insertar un separador
3
Insertar un menú anidado
4
Eliminar un elemento del menú
5
Desactivar un elemento del menú
6
Elemento de menú con casilla de selección
7
Invertir el botón de opción

Para crear tu primer elemento de menú, escribe «Opciones A» en la casilla A de arriba y, a continuación, debajo, en este orden: A1, A2, separador, A3, A4.

Image

A continuación, al lado:

Image

Utiliza la herramienta 3 para indicar que B3 es un submenú anidado.

A medida que se va diseñando el menú, la estructura lógica de nuestra ventana va evolucionando:

Image

Si ejecutamos ahora nuestra aplicación, veremos una ventana vacía sin menú. Tenemos que asociar el menú creado a nuestra ventana. Para ello, en la estructura de la ventana, selecciona el objeto this:

Image

A continuación, tendrá acceso a las propiedades de this:

Image

Una de ellas es JMenuBar, que sirve para establecer el menú que se asociará a la ventana. Haga clic en la celda situada a la derecha de JMenuBar. A continuación, se le mostrarán todos los menús creados. En este caso, solo tendremos jMenuBar1. Selecciónalo.

Ejecute la aplicación (F9):

Image

Ahora tenemos un menú, pero las opciones, por el momento, no hacen nada. Las opciones del menú se tratan como componentes: tienen propiedades y eventos. En la estructura del menú, selecciona la opción jMenuItem1:

Image

De este modo, tendrá acceso a sus propiedades y eventos:

Image

Seleccione la página de eventos y haga clic en la celda situada a la derecha del evento actionPerformed: este es el evento que se produce al hacer clic en un elemento del menú. Se le propone un procedimiento de tratamiento por defecto. Haga doble clic en él para acceder a su código:

Image

Escribiremos el siguiente código sencillo:

  void jMenuItem1_actionPerformed(ActionEvent e) {
    afficher("L'option A1 a été sélectionnée");
  }

  void afficher(String message){
     // muestra un mensaje en un cuadro de diálogo
    JOptionPane.showMessageDialog(this,message,"Menus",JOptionPane.INFORMATION_MESSAGE);
  }//mostrar

Ejecuta la aplicación y selecciona la opción A1 para obtener el siguiente mensaje:

Image

El código útil de esta aplicación es el siguiente:

.....

public class Cadre1 extends JFrame {
  JPanel contentPane;
  BorderLayout borderLayout1 = new BorderLayout();
  JMenuBar jMenuBar1 = new JMenuBar();
  JMenu jMenu1 = new JMenu();
  JMenuItem jMenuItem1 = new JMenuItem();
  JMenuItem jMenuItem2 = new JMenuItem();
  JMenuItem jMenuItem3 = new JMenuItem();
  JMenuItem jMenuItem4 = new JMenuItem();
  JMenu jMenu2 = new JMenu();
  JMenuItem jMenuItem5 = new JMenuItem();
  JMenuItem jMenuItem6 = new JMenuItem();
  JMenu jMenu3 = new JMenu();
  JMenuItem jMenuItem7 = new JMenuItem();
  JMenuItem jMenuItem8 = new JMenuItem();
  JMenuItem jMenuItem9 = new JMenuItem();

  /**Crear el marco*/
  public Cadre1() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
   /**Inicializar el componente*/
  private void jbInit() throws Exception  {
        // la ventana está asociada a un menú
    this.setJMenuBar(jMenuBar1);

    jMenu1.setText("Options A");
    jMenuItem1.setText("A1");
    jMenuItem1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        jMenuItem1_actionPerformed(e);
      }
    });
    jMenuItem2.setText("A2");
    jMenuItem3.setText("A3");
    jMenuItem4.setText("A4");
    jMenu2.setText("Options B");
    jMenuItem5.setText("B1");
    jMenuItem6.setText("B2");
    jMenu3.setText("B3");
    jMenuItem7.setText("B31");
    jMenuItem8.setText("B32");
    jMenuItem9.setText("B4");
    jMenuBar1.add(jMenu1);
    jMenuBar1.add(jMenu2);
    jMenu1.add(jMenuItem1);
    jMenu1.add(jMenuItem2);
    jMenu1.addSeparator();
    jMenu1.add(jMenuItem3);
    jMenu1.add(jMenuItem4);
    jMenu2.add(jMenuItem5);
    jMenu2.add(jMenuItem6);
    jMenu2.add(jMenu3);
    jMenu2.add(jMenuItem9);
    jMenu3.add(jMenuItem7);
    jMenu3.add(jMenuItem8);
  }
   /**Sustituido, para que podamos salir cuando se cierre la ventana*/
  protected void processWindowEvent(WindowEvent e) {
....
  }

  void jMenuItem1_actionPerformed(ActionEvent e) {
    afficher("L'option A1 a été sélectionnée");
  }

  void afficher(String message){
     // muestra un mensaje en un cuadro de diálogo
    JOptionPane.showMessageDialog(this,message,"Menus",JOptionPane.INFORMATION_MESSAGE);
  }//Mostrar

}

5.3.1. Cuadros de mensaje

Ya hemos utilizado la clase JOptionPane para mostrar mensajes. Así, el siguiente código:

import javax.swing.*;

public class dialog1 {
  public static void main(String arg[]){
    JOptionPane.showMessageDialog(null,"Un message","Titre de la boîte",JOptionPane.INFORMATION_MESSAGE);
  }
}

muestra el siguiente cuadro:

Image

Al cerrar esta ventana, esta desaparece, pero el hilo de ejecución en el que se ejecutaba no se detiene. Este fenómeno no suele producirse. Los cuadros de diálogo se utilizan dentro de una aplicación que, en algún momento, utiliza una instrucción System.exit(n) para detener todos los hilos. Lo tendremos en cuenta en los ejemplos siguientes, todos ellos basados en el mismo modelo. En DOS, la aplicación se puede interrumpir con Ctrl-C. Con JBuilder, se utilizará la opción Ejecutar/Reiniciar el programa (Ctrl-F2). Por otra parte, el primer argumento de showMessageDialog es aquí null. Por lo general, no es así. Suele ser this, donde this designa la ventana principal de la aplicación.

5.3.2. Aspecto y comportamiento

El aspecto del cuadro anterior podría ser diferente. Es posible configurar este aspecto mediante la clase javax.swing.UIManager. Cuando comentamos el código generado por JBuilder para nuestra primera ventana, nos encontramos con una instrucción en la que no nos detuvimos:

      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

El método setLookAndFeel de la clase UIManager (UI = Interfaz de usuario) permite definir el aspecto de las interfaces gráficas. La clase UIManager dispone de un método que permite conocer las «apariencias» posibles para las interfaces:

static UIManager.LookAndFeelInfo[]
getInstalledLookAndFeels()

El método devuelve una matriz de elementos de tipo LookAndFeelInfo. Esta clase tiene un método:

String
getClassName()

que devuelve el nombre de la clase que «implementa» un aspecto determinado. Probemos el siguiente programa:

import javax.swing.*;

public class LookAndFeels {
  // muestra los «look and feels» disponibles
  public static void main(String[] args) {
    // lista los «look and feels» instalados
    UIManager.LookAndFeelInfo[] lf=UIManager.getInstalledLookAndFeels();
    // visualización
    for(int i=0;i<lf.length;i++){
      System.out.println(lf[i].getClassName());
    }//para
  }//principal
}//clasifica

Da los siguientes resultados:

javax.swing.plaf.metal.MetalLookAndFeel
com.sun.java.swing.plaf.motif.MotifLookAndFeel
com.sun.java.swing.plaf.windows.WindowsLookAndFeel

Por lo tanto, parece que hay tres «aspectos» diferentes. Volvamos a nuestro programa de visualización de mensajes probando los diferentes aspectos posibles:

import javax.swing.*;

public class LookAndFeel2 {
  // muestra los «look and feels» disponibles
  public static void main(String[] args) {
    // lista de los «look and feels» instalados
    UIManager.LookAndFeelInfo[] lf=UIManager.getInstalledLookAndFeels();
    // visualización
    for(int i=0;i<lf.length;i++){
      // gestor de apariencia
      try{
        UIManager.setLookAndFeel(lf[i].getClassName());
      }catch(Exception ex){
        System.err.println(ex.getMessage());
      }//captura
      // mensaje
      JOptionPane.showMessageDialog(null,"Un message",lf[i].getClassName(),JOptionPane.INFORMATION_MESSAGE);
    }//for
  }//main
}//clase

Al ejecutarlo, se obtienen los siguientes resultados:

que corresponden, de derecha a izquierda, a los «estilos» Metal, Motivo y Windows.

5.3.3. Cuadros de confirmación

La clase JOptionPane dispone de un método showConfirmDialog para mostrar cuadros de confirmación con los botones Oui, Non y Annuler. Hay varios métodos showConfirmDialog sobrescritos. Analizamos uno de ellos:

static int
showConfirmDialog(Component parentComponent, Object message, String title, int optionType)
parentComponent
el componente padre del cuadro de diálogo. A menudo es la ventana o el valor null
message
el mensaje que se va a mostrar
title
el título del cuadro de diálogo
optionType
JOptionPane.YES_NO_OPTION: botones «Sí», «No»
JOptionPane.YES_NO_CANCEL_OPTION: botones «Sí», «No», «Cancelar»

El resultado devuelto por el método es:

JOptionPane.YES_OPTION
el usuario ha hecho clic en «Sí»
JOptionPane.NO_OPTION
El usuario ha hecho clic en «No»
JOptionPane.CANCEL_OPTION
El usuario ha hecho clic en «Cancelar»
JOptionPane.CLOSED_OPTION
El usuario ha cerrado el cuadro de diálogo

He aquí un ejemplo:

import javax.swing.*;

public class confirm1 {
  public static void main(String[] args) {
    // muestra cuadros de confirmación
    int réponse;
    affiche(JOptionPane.showConfirmDialog(null,"Voulez-vous continuer ?","Confirmation",JOptionPane.YES_NO_OPTION));
    affiche(JOptionPane.showConfirmDialog(null,"Voulez-vous continuer ?","Confirmation",JOptionPane.YES_NO_CANCEL_OPTION));
  }//principal

  private static void affiche(int réponse){
     // indica qué tipo de respuesta se ha recibido
    switch(réponse){
      case JOptionPane.YES_OPTION :
        System.out.println("Oui");
        break;
      case JOptionPane.NO_OPTION :
        System.out.println("Non");
        break;
      case JOptionPane.CANCEL_OPTION :
        System.out.println("Annuler");
        break;
      case JOptionPane.CLOSED_OPTION :
        System.out.println("Fermeture");
        break;
    }//interruptor
  }//pantalla
}//clasificar

En la consola aparecen los mensajes Non y Annuler.

5.3.4. Campo de entrada

La clase JOptionPane también ofrece la posibilidad de realizar una entrada mediante el método showInputDialog. Una vez más, existen varios métodos sobrecargados. A continuación, presentamos uno de ellos:

static String
showInputDialog(Component parentComponent, Object message, String title, int messageType)

Los argumentos son los mismos que ya hemos visto en varias ocasiones. El método devuelve la cadena de caracteres introducida por el usuario. A continuación se muestra un ejemplo:

import javax.swing.*;

public class input1 {
  public static void main(String[] args) {
    // introducción
    System.out.println("Chaîne saisie ["+
      JOptionPane.showInputDialog(null,"Quel est votre nom","Saisie du nom",JOptionPane.QUESTION_MESSAGE )
      + "]");
  }//mano
}//clase

La visualización del cuadro de entrada:

Image

Visualización de la consola:

Chaîne saisie [dupont]

5.4. Cuadros de selección

Ahora nos centraremos en varios cuadros de selección predefinidos en Java 2:

JFileChooser
cuadro de selección que permite seleccionar un archivo en el árbol de archivos
JColorChooser
cuadro de selección que permite elegir un color

5.4.1. Cuadro de selección JFileChooser

Vamos a crear la siguiente aplicación:

Image

Los controles son los siguientes:

N.º
tipo
nombre
función
0
JScrollPane
jScrollPane1
Contenedor deslizante para el cuadro de texto 1
1
JTextArea, que a su vez se encuentra dentro de JScrollPane
txtTexte
Texto escrito por el usuario o cargado desde un archivo
2
JButton
btnSauvegarder
permite guardar el texto de 1 en un archivo de texto
3
JButton
btnCharger
permite cargar el contenido de un archivo de texto en 1
4
JButton
btnEffacer
borra el contenido de 1

Se utiliza un control no visual: jFileChooser1. Este se toma de la paleta de contenedores Swing de JBuilder:

Image

Colocamos el componente en la ventana de diseño, pero fuera del formulario. Aparece en la lista de componentes:

Image

A continuación, mostramos el código relevante del programa para tener una visión general:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.io.*;

public class dialogues extends JFrame {
   // los componentes del marco
  JPanel contentPane;
  JButton btnSauvegarder = new JButton();
  JButton btnCharger = new JButton();
  JButton btnEffacer = new JButton();
  JScrollPane jScrollPane1 = new JScrollPane();
  JTextArea txtTexte = new JTextArea();
  JFileChooser jFileChooser1 = new JFileChooser();

  // los filtros de archivos
  javax.swing.filechooser.FileFilter filtreTxt = null;

   //Crear el marco
  public dialogues() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
       // otras inicializaciones
      moreInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }

   // moreInit
  private void moreInit(){
     // inicializaciones al crear la ventana
     // filtro *.txt
    filtreTxt = new javax.swing.filechooser.FileFilter(){
      public boolean accept(File f){
        // ¿Se acepta «f»?
        return f.getName().toLowerCase().endsWith(".txt");
      }//aceptar
      public String getDescription(){
        // descripción del filtro
        return "Fichiers Texte (*.txt)";
      }//getDescription
    };
     // se añade el filtro
    jFileChooser1.addChoosableFileFilter(filtreTxt);
    // también queremos el filtro de todos los archivos
    jFileChooser1.setAcceptAllFileFilterUsed(true);
    // se establece el directorio de origen de la caja FileChooser en el directorio actual
    jFileChooser1.setCurrentDirectory(new File("."));
  }

   //Inicializar el componente
  private void jbInit() throws Exception  {
.......................
  }
   //Sustituido, así podemos salir cuando se cierre la ventana
  protected void processWindowEvent(WindowEvent e) {
......................
    }
  }

  void btnCharger_actionPerformed(ActionEvent e) {
    // Selección de un archivo mediante un objeto JFileChooser

     // Se establece el filtro inicial
    jFileChooser1.setFileFilter(filtreTxt);
     // se muestra el cuadro de selección
    int returnVal = jFileChooser1.showOpenDialog(this);
    // ¿Ha seleccionado algo el usuario?
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      // se introduce el archivo en el campo de texto
       lireFichier(jFileChooser1.getSelectedFile());
    }//si
  }//btnCharger_actionPerformed

  // lireFichier
  private void lireFichier(File fichier){
     // muestra el contenido del archivo de texto «fichier» en el campo de texto

     // se borra el campo de texto
    txtTexte.setText("");

     // algunos datos
    BufferedReader IN=null;
    String ligne=null;
    try{
      // apertura del archivo en modo de lectura
      IN=new BufferedReader(new FileReader(fichier));
      // se lee el archivo línea por línea
      while((ligne=IN.readLine())!=null){
        txtTexte.append(ligne+"\n");
      }//while
       // cierre del archivo
      IN.close();
    }catch(Exception ex){
       // se ha producido un error
      txtTexte.setText(""+ex);
    }//catch
  }

  // borrar
  void btnEffacer_actionPerformed(ActionEvent e) {
    // se borra el cuadro de texto
    txtTexte.setText("");
  }

   // guardar
  void btnSauvegarder_actionPerformed(ActionEvent e) {
     // guarda el contenido del cuadro de texto en un archivo

     // se establece el filtro inicial
    jFileChooser1.setFileFilter(filtreTxt);
     // se muestra el cuadro de selección de guardado
    int returnVal = jFileChooser1.showSaveDialog(this);
    // ¿Ha seleccionado algo el usuario?
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      // se escribe el contenido del cuadro de texto en un archivo
       écrireFichier(jFileChooser1.getSelectedFile());
    }//si
  }

   // lireFichier
  private void écrireFichier(File fichier){
     // escribe el contenido del cuadro de texto en un archivo

     // algunos datos
    PrintWriter PRN=null;
    try{
       // abre el archivo en modo de escritura
      PRN=new PrintWriter(new FileWriter(fichier));
      // se escribe el contenido del cuadro de texto
      PRN.print(txtTexte.getText());
       // cierre del archivo
      PRN.close();
    }catch(Exception ex){
       // se ha producido un error
      txtTexte.setText(""+ex);
    }//captura
  }

No comentaremos el código de los métodos btnEffacer_Click, lireFichier y écrireFichier, ya que no aportan nada que no hayamos visto ya. Nos centraremos en la clase JFileChooser y su uso. Esta clase es compleja, del tipo «complicadísima». Aquí solo utilizamos los siguientes métodos:

addChoosableFilter(FileFilter)
establece los tipos de archivo que se ofrecen para la selección
setAcceptAllFileFilterUsed(boolean)
indica si el tipo «Todos los archivos» debe ofrecerse para la selección o no
File getSelectedFile()
el archivo (File) seleccionado por el usuario
int showSaveDialog()
método que muestra el cuadro de diálogo de selección de guardado. Devuelve un resultado de tipo int. El valor jFileChooser.APPROVE_OPTION indica que el usuario ha realizado una selección válida. De lo contrario, o bien ha cancelado la selección, o bien se ha producido un error.
setCurrentDirectory
para establecer el directorio inicial desde el que el usuario comenzará a explorar el sistema de archivos
setFileFilter(FileFilter)
para establecer el filtro activo

El método showSaveDialog muestra un cuadro de selección similar al siguiente:

Image

1
lista desplegable creada con el método addChoosableFilter. Contiene lo que se denomina filtros de selección, representados por la clase FileFilter. Es el desarrollador quien debe definir estos filtros y añadirlos a la lista 1.
2
carpeta actual, establecida mediante el método setCurrentDirectory si se ha utilizado dicho método; de lo contrario, la carpeta actual será la carpeta «Mis documentos» en Windows o el directorio de inicio en Unix.
3
nombre del archivo seleccionado o introducido directamente por el usuario. Estará disponible con el método getSelectedFile()
4
Botones «Guardar»/«Cancelar». Si se utiliza el botón Enregistrer, el método showSaveDialog devuelve el resultado jFileChooser.APPROVE_OPTION

¿Cómo se crean los filtros de archivos de la lista desplegable 1? Los filtros se añaden a la lista 1 mediante el método:

addChoosableFilter(FileFilter)
establece los tipos de archivo que se ofrecen para la selección

de la clase JFileChooser. Nos queda por conocer la clase FileFilter. En realidad, se trata de la clase javax.swing.filechooser.FileFilter, que es una clase abstracta, c.a.d, una clase que no puede instanciarse, sino solo derivarse. Se define de la siguiente manera:

FileFilter()
constructor
boolean accept(File)
indica si el archivo f pertenece al filtro o no
String getDescription()
cadena de descripción del filtro

Veamos un ejemplo. Queremos que el menú desplegable 1 ofrezca un filtro para seleccionar los archivos *.txt con la descripción «Archivos de texto (*.txt)».

  • Tenemos que crear una clase derivada de la clase FileFilter
  • utilizar el método boolean accept(File f) para devolver el valor true si el nombre del archivo f termina en .txt
  • utilizar el método String getDescription() para devolver la descripción «Archivos de texto (*.txt)»

Este filtro se podría definir en nuestra aplicación de la siguiente manera:

  javax.swing.filechooser.FileFilter filtreTxt = new javax.swing.filechooser.FileFilter(){
    public boolean accept(File f){
      // ¿Se acepta «f»?
      return f.getName().toLowerCase().endsWith(".txt");
    }//aceptado
    public String getDescription(){
      // descripción del filtro
      return "Fichiers Texte (*.txt)";
    }//getDescription
  };

Este filtro se añadiría a la lista de filtros del objeto jFileChooser1 mediante la instrucción:

     // se añade el filtro *.txt
    jFileChooser1.addChoosableFileFilter(filtretxt);

Todo esto, junto con algunas otras inicializaciones, se lleva a cabo en el método moreInit, que se ejecuta al crear la ventana (véase el programa completo más arriba). El código del botón Sauvegarder es el siguiente:

  void btnSauvegarder_actionPerformed(ActionEvent e) {
    // guarda el contenido del cuadro de texto en un archivo

     // se establece el filtro inicial
    jFileChooser1.setFileFilter(filtreTxt);
     // se muestra el cuadro de selección de guardado
    int returnVal = jFileChooser1.showSaveDialog(this);
    // ¿Ha seleccionado algo el usuario?
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      // se escribe el contenido del cuadro de texto en un archivo
       écrireFichier(jFileChooser1.getSelectedFile());
    }//si
  }

La secuencia de operaciones es la siguiente:

  • se establece el filtro activo en *.txt para permitir al usuario buscar preferentemente este tipo de archivos. También está presente el filtro «Todos los archivos». Se ha añadido en el procedimiento moreInit. Por lo tanto, hay dos filtros.
  • Se muestra el cuadro de selección de guardado. Aquí perdemos el control, ya que el usuario utiliza el cuadro de selección para indicar un archivo del sistema de archivos.
  • Al salir del cuadro de selección, se comprueba el valor de retorno para ver si hay que guardar o no el cuadro de texto. En caso afirmativo, debe guardarse en el archivo obtenido mediante el método getSelectedFile.

El código relacionado con el botón «Cargar» es muy similar al del botón «Guardar».

  void btnCharger_actionPerformed(ActionEvent e) {
    // selección de un archivo mediante un objeto JFileChooser

     // se establece el filtro inicial
    jFileChooser1.setFileFilter(filtreTxt);
     // se muestra el cuadro de selección
    int returnVal = jFileChooser1.showOpenDialog(this);
    // ¿Ha seleccionado algo el usuario?
    if(returnVal == JFileChooser.APPROVE_OPTION) {
      // se introduce el archivo en el campo de texto
       lireFichier(jFileChooser1.getSelectedFile());
    }//si
  }//btnCharger_actionPerformed

Hay dos diferencias:

  • para mostrar el cuadro de selección de archivos, se utiliza el método showOpenDialog en lugar del método showSaveDialog. El cuadro de selección que se muestra es similar al que muestra el método showSaveDialog.
  • Si el usuario ha seleccionado correctamente un archivo, se llama al método lireFichier en lugar del método écrireFichier.

5.4.1.1. Cuadros de selección JColorChooser y JFontChooser

Continuamos con el ejemplo anterior añadiendo dos nuevos botones:

Image

N.º
tipo
nombre
función
6
JButton
btnCouleur
para establecer el color de los caracteres de TextBox
7
JButton
btnPolice
para establecer la fuente de TextBox

El componente JColorChooser, que permite mostrar un cuadro de selección de color, se encuentra en la lista de componentes Swing de JBuilder:

Image

Colocamos este componente en la ventana de diseño, pero fuera del formulario. No es visible en el formulario, pero sí aparece en la lista de componentes de la ventana:

Image

La clase JColorChooser es muy sencilla. Mostramos el cuadro de selección de colores con el método int showDialog:

static Color
showDialog(Component component, String title, Color initialColor)
Component component
el componente padre del cuadro de selección, normalmente una ventana JFrame
String title
el título que aparece en la barra de título del cuadro de selección
Color intialColor
color seleccionado inicialmente en el cuadro de selección

Si el usuario elige un color, el método devuelve un color; en caso contrario, devuelve el valor null. El código asociado al botón Couleur es el siguiente:

  void btnCouleur_actionPerformed(ActionEvent e) {
     // selección de un color de texto mediante el componente JColorChooser
    Color couleur;
    if((couleur=jColorChooser1.showDialog(this,"Choix d'une couleur",Color.BLACK))!=null){
       // se establece el color de los caracteres del cuadro de texto
      txtTexte.setForeground(couleur);
    }//if
  }

La clase Color se define en java.awt.Color. En ella se definen varias constantes, entre ellas Color.BLACK para el color negro. El cuadro de selección que se muestra es el siguiente

Image

Curiosamente, la biblioteca Swing no ofrece ninguna clase para seleccionar un tipo de letra. Afortunadamente, existen recursos Java en Internet. Al realizar una búsqueda con la palabra clave «JFontChooser», que parece un nombre posible para una clase de este tipo, se encuentran varios resultados. El ejemplo que viene a continuación nos dará la oportunidad de configurar JBuilder para que utilice paquetes no previstos en su configuración inicial.

El paquete recuperado se llama swingextras.jar y se ha colocado en la carpeta <jdk>\jre\lib\perso, donde <jdk> designa el directorio de instalación del JDK utilizado por JBuilder. Podría haberse colocado en cualquier otro lugar.

Veamos el contenido del paquete SwingExtras.jar:

Image

En él se encuentra el código fuente Java de los componentes que incluye el paquete. También se encuentran las propias clases:

Image

De este archivo, cabe destacar que la clase JFontChooser se llama en realidad com.lamatek.swingextras.JFontChooser. Si no queremos escribir el nombre completo, tendremos que escribir al principio del programa:

import com.lamatek.swingextras.*;

¿Cómo encontrarán el compilador y la máquina virtual de Java este nuevo paquete? En el caso del uso directo de JDK, esto ya se ha explicado y el lector podrá consultar las explicaciones en el apartado sobre los paquetes de Java. A continuación detallamos el método que hay que utilizar con JBuilder. Los paquetes se configuran en el menú Opciones/Configurar los JDK:

Image

1
El botón Modifier (1) permite indicar a JBuilder qué JDK se va a utilizar. En este ejemplo, JBuilder había traído consigo el JDK 1.3.1. Cuando se lanzó el JDK 1.4, se instaló por separado del JBuilder y se utilizó el botón Modifier para indicar al JBuilder queutilizara a partir de entonces el JDK 1.4, indicando el directorio de instalación de este último
2
directorio raíz de JDK, que actualmente utiliza JBuilder
3
lista de archivos Java (.jar) utilizados por JBuilder. Se pueden añadir otros mediante el botón Ajouter (4)
4
El botón Ajouter (4) permite añadir nuevos paquetes que JBuilder utilizará tanto para la compilación como para la ejecución de los programas. Aquí lo hemos utilizado para añadir el paquete SwingExtras.jar (5)

Ahora JBuilder está correctamente configurado para poder utilizar la clase JFontChooser. Sin embargo, necesitaríamos tener acceso a la definición de esta clase para utilizarla correctamente. El archivo swingextras.jar contiene archivos HTML que podríamos extraer para aprovecharlos. No es necesario. Se puede acceder directamente a la documentación de Java incluida en los archivos .jar desde JBuilder. Para ello, hay que configurar la pestaña Documentation (6) que aparece más arriba. Aparecerá la siguiente ventana:

Image

El botón Ajouter nos permite indicar que el archivo SwingExtras.jar debe explorarse en busca de documentación. Una vez validado este paso, podemos comprobar que efectivamente tenemos acceso a la documentación de SwingExtras.jar. Esto se traduce en varias ventajas:

  • si empezamos a escribir la instrucción de importación
import com.lamatek.swingextras.*;

, se observará que JBuilder nos ofrece ayuda:

Image

El paquete com.lamatek se ha encontrado correctamente.

  • Si ahora pasamos al siguiente programa:
import com.lamatek.swingextras.*;

public class test{
  JFontChooser jFontChooser1=null;
}  

Al introducir «F1» en la palabra clave «JFontChooser», se obtiene ayuda sobre esta clase:

Image

En la barra de estado 1 se puede ver que, efectivamente, se está utilizando un archivo HTML del paquete swingextras.jar. El ejemplo anterior es lo suficientemente claro como para que podamos escribir el código del botón Police de nuestra aplicación:

  void btnPolice_actionPerformed(ActionEvent e) {
    // selección de una fuente de texto mediante el componente JFontChooser
    jFontChooser1 = new JFontChooser(new Font("Arial", Font.BOLD, 12));
    if (jFontChooser1.showDialog(this, "Choix d'une police") == JFontChooser.ACCEPT_OPTION) {
      // se cambia la fuente de los caracteres del cuadro de texto
      txtTexte.setFont(jFontChooser1.getSelectedFont());
    }//si
  }

El cuadro de selección que muestra el método showDialog anterior es el siguiente:

Image

5.5. La aplicación gráfica IMPOTS

Retomamos la aplicación IMPOTS, que ya hemos tratado en dos ocasiones. Ahora le añadimos una interfaz gráfica:

Image

Los controles son los siguientes

n.º
tipo
nombre
función
1
JRadioButton
rdOui
marcar si está casado
2
JRadioButton
rdNon
marcar si no está casado
3
JSpinner
spinEnfants
Número de hijos del contribuyente
Mínimo=0, Máximo=20, Incremento=1
4
JTextField
txtSalaire
salario anual del contribuyente en F
5
JLabel
lblImpots
Importe del impuesto a pagar
6
JTextField
txtStatus
Campo de mensajes de estado: no modificable

El menú es el siguiente:

opción principal
opción secundaria
nombre
función
Impuestos
   
 
Inicializar
mnuInitialiser
carga los datos necesarios para el cálculo a partir de un archivo de texto
 
Calcular
mnuCalculer
calcula el impuesto a pagar cuando todos los datos necesarios están presentes y son correctos
 
Borrar
mnuEffacer
restablece el formulario a su estado inicial
 
Salir
mnuQuitter
cierra la aplicación

Reglas de funcionamiento

  • El menú Calculer permanece desactivado mientras no haya nada en el campo del salario
  • si, al iniciar el cálculo, resulta que el salario es incorrecto, se señala el error:

Image

A continuación se muestra el programa. Utiliza la clase impots creada en el capítulo sobre clases. No se ha reproducido aquí parte del código generado automáticamente por JBuilder.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.io.*;
import java.util.*;
import javax.swing.event.*;

public class frmImpots extends JFrame {
  // los componentes de la ventana
  JPanel contentPane;
  JMenuBar jMenuBar1 = new JMenuBar();
  JMenu jMenu1 = new JMenu();
  JMenuItem mnuInitialiser = new JMenuItem();
  JMenuItem mnuCalculer = new JMenuItem();
  JMenuItem mnuEffacer = new JMenuItem();
  JMenuItem mnuQuitter = new JMenuItem();
  JLabel jLabel1 = new JLabel();
  JFileChooser jFileChooser1 = new JFileChooser();
  JTextField txtStatus = new JTextField();
  JRadioButton rdOui = new JRadioButton();
  JRadioButton rdNon = new JRadioButton();
  JLabel jLabel2 = new JLabel();
  JLabel jLabel3 = new JLabel();
  JTextField txtSalaire = new JTextField();
  JLabel jLabel4 = new JLabel();
  JLabel jLabel5 = new JLabel();
  JLabel lblImpots = new JLabel();
  JLabel jLabel7 = new JLabel();
  ButtonGroup buttonGroup1 = new ButtonGroup();
  JSpinner spinEnfants=null;
  FileFilter filtreTxt=null;

   // los atributos de la clase
  double[] limites=null;
  double[] coeffr=null;
  double[] coeffn=null;
  impots objImpots=null;

   //Crear el marco
  public frmImpots() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // otras inicializaciones
    moreInit();
  }

   // inicialización del formulario
  private void moreInit(){
     // menú «Calcular» desactivado
    mnuCalculer.setEnabled(false);
    // campos de entrada desactivados
    txtSalaire.setEditable(false);
    txtSalaire.setBackground(Color.WHITE);
    // campo de estado
    txtStatus.setBackground(Color.WHITE);
    // selector «Hijos»: entre 0 y 20 hijos
    spinEnfants=new JSpinner(new SpinnerNumberModel(0,0,20,1));
    spinEnfants.setBounds(new Rectangle(137,60,40,27));
    contentPane.add(spinEnfants);
     // filtro *.txt para el cuadro de selección
    filtreTxt = new javax.swing.filechooser.FileFilter(){
      public boolean accept(File f){
        // ¿Se acepta «f»?
        return f.getName().toLowerCase().endsWith(".txt");
      }//aceptar
      public String getDescription(){
        // descripción del filtro
        return "Fichiers Texte (*.txt)";
      }//getDescription
    };
     // se añade el filtro *.txt
    jFileChooser1.addChoosableFileFilter(filtreTxt);
    // también queremos el filtro para todos los archivos
    jFileChooser1.setAcceptAllFileFilterUsed(true);
    // se establece el directorio de origen del buzón FileChooser
    jFileChooser1.setCurrentDirectory(new File("."));
  }//moreInit

   //Inicializar el componente
  private void jbInit() throws Exception  {

................

  }

   //Sustituido, así podemos salir cuando se cierre la ventana
  protected void processWindowEvent(WindowEvent e) {
.....................
  }

  void mnuQuitter_actionPerformed(ActionEvent e) {
    // salimos de la aplicación
    System.exit(0);
  }

  void mnuInitialiser_actionPerformed(ActionEvent e) {
    // se carga el archivo de datos
     // que se selecciona con el cuadro de selección JFileChooser1
    jFileChooser1.setFileFilter(filtreTxt);
    if (jFileChooser1.showOpenDialog(this)!=JFileChooser.APPROVE_OPTION)
      return;
     // se procesa el archivo seleccionado
    try{
       // lectura de datos
      lireFichier(jFileChooser1.getSelectedFile());
       // creación del objeto «impuestos»
      objImpots=new impots(limites,coeffr,coeffn);
       // confirmación
      txtStatus.setText("Données chargées");
       // el salario se puede modificar
      txtSalaire.setEditable(true);
       // No se pueden realizar más cambios
      mnuInitialiser.setEnabled(false);
    }catch(Exception ex){
       // problema
      txtStatus.setText("Erreur : " + ex.getMessage());
      // fin
      return;
    }//captura
  }

  private void lireFichier(File fichier) throws Exception {
     // las tablas de datos
    ArrayList aLimites=new ArrayList();
    ArrayList aCoeffR=new ArrayList();
    ArrayList aCoeffN=new ArrayList();
    String[] champs=null;

     // apertura del archivo en modo lectura
    BufferedReader IN=new BufferedReader(new FileReader(fichier));
    // se lee el archivo línea por línea
     // tienen la forma límite coeffr coeffn
    String ligne=null;
    int numLigne=0;           // N.º de línea actual
    while((ligne=IN.readLine())!=null){
      // una línea más
      numLigne++;
       // se descompone la línea en campos
      champs=ligne.split("\\s+");
       // ¿3 campos?
      if(champs.length!=3)
        throw new Exception("ligne " + numLigne + "erronée dans fichier des données");
       // se recuperan los tres campos
       aLimites.add(champs[0]);
       aCoeffR.add(champs[1]);
       aCoeffN.add(champs[2]);
    }//while
     // cierre del archivo
    IN.close();
     // transferencia de los datos a matrices delimitadas
    int n=aLimites.size();
    limites=new double[n];
    coeffr=new double[n];
    coeffn=new double[n];
    for(int i=0;i<n;i++){
      limites[i]=Double.parseDouble((String)aLimites.get(i));
      coeffr[i]=Double.parseDouble((String)aCoeffR.get(i));
      coeffn[i]=Double.parseDouble((String)aCoeffN.get(i));
    }//for
  }

  void mnuCalculer_actionPerformed(ActionEvent e) {
    // cálculo del impuesto
     // verificación del salario
    int salaire=0;
    try{
      salaire=Integer.parseInt(txtSalaire.getText().trim());
      if(salaire<0) throw new Exception();
    }catch (Exception ex){
       // mensaje de error
      txtStatus.setText("Salaire incorrect. Recommencez");
       // foco en el campo erróneo
      txtSalaire.requestFocus();
       // volver a la interfaz
      return;
    }
     // número de hijos
    Integer InbEnfants=(Integer)spinEnfants.getValue();
     // cálculo de los impuestos
    lblImpots.setText(""+objImpots.calculer(rdOui.isSelected(),InbEnfants.intValue(),salaire));
     // borrado del mensaje de estado
    txtStatus.setText("");
  }

  void txtSalaire_caretUpdate(CaretEvent e) {
     // el salario ha cambiado: se actualiza el menú «Calcular»
    mnuCalculer.setEnabled(! txtSalaire.getText().trim().equals(""));
  }

  void mnuEffacer_actionPerformed(ActionEvent e) {
    // borrado del formulario
    rdNon.setSelected(true);
    spinEnfants.getModel().setValue(new Integer(0));
    txtSalaire.setText("");
    mnuCalculer.setEnabled(false);
    lblImpots.setText("");
  }
}

Aquí hemos utilizado un componente disponible a partir de la versión 1.4 de JDK, el JSpinner. Se trata de un incrementador que, en este caso, permite al usuario establecer el número de hijos. Este componente Swing no estaba disponible en la barra de componentes Swing de JBuilder 6, utilizada para la prueba. Esto no impide su uso, aunque el proceso resulte un poco más complicado que con los componentes disponibles en la barra de componentes. De hecho, no se puede colocar el componente JSpinner en el formulario durante el diseño. Hay que hacerlo en tiempo de ejecución. Veamos el código que lo hace:

     // Control de carga «Hijos»: entre 0 y 20 hijos
    spinEnfants=new JSpinner(new SpinnerNumberModel(0,0,20,1));
    spinEnfants.setBounds(new Rectangle(137,60,40,27));
    contentPane.add(spinEnfants);

La primera línea crea el componente JSpinner. Este componente puede utilizarse para diversas cosas, no solo como un incrementador de números enteros, como en este caso. El argumento del constructor JSpinner es, en este caso, un modelo de incrementador de números que admite cuatro parámetros (valor, mínimo, máximo e incremento). El componente tiene la siguiente forma:

Image

valeur
valor inicial mostrado en el componente
min
valor mínimo que se puede mostrar en el componente
max
valor máximo que se puede mostrar en el componente
incrément
valor de incremento del valor mostrado al utilizar las flechas arriba/abajo del componente

El valor del componente se obtiene mediante su método getValue, que devuelve un tipo Object. De ahí que haya que realizar algunas conversiones de tipo para obtener el entero que necesitamos:

     // Número de hijos
    Integer InbEnfants=(Integer)spinEnfants.getValue();
     // Cálculo de los impuestos
    lblImpots.setText(""+objImpots.calculer(rdOui.isSelected(),
      InbEnfants.intValue(),salaire));

Una vez definido el componente JSpinner, se coloca en la ventana:

    spinEnfants.setBounds(new Rectangle(137,60,40,27));
    contentPane.add(spinEnfants);

Antes de nada, el usuario debe utilizar la opción de menú Initialiser, que crea un objeto impots con el constructor

    public impots(double[] LIMITES, double[] COEFFR, double[] COEFFN) throws Exception

de la clase impots. Recordemos que esta clase se definió a modo de ejemplo en el capítulo dedicado a las clases. Se puede consultar dicho capítulo si es necesario. Las tres tablas necesarias para el constructor se rellenan a partir del contenido de un archivo de texto con el siguiente formato:

12620.0
0
0
13190
0,05
631
15 640
0,1
1290,5
24 740
0,15
2072,5
31 810
0,2
3309,5
39 970
0,25
4900
48 360
0,3
6898,5
55 790
0,35
9316,5
92 970
0,4
12 106
127 860
0,45
16 754,5
151 250
0,50
23 147,5
172 040
0,55
30 710
195 000
0,60
39 312
0
0,65
49 062

Cada línea contiene tres números separados por al menos un espacio. El usuario selecciona este archivo de texto mediante un componente JFileChooser. Una vez creado el objeto de tipo impots, solo queda dejar que el usuario introduzca los tres datos necesarios: si está casado o no, el número de hijos y el salario anual, y llamar al método calculer de la clase impots. La operación se puede repetir varias veces. El objeto de tipo impots, por su parte, solo se crea una vez, al utilizar la opción Initialiser.

5.6. Creación de applets

5.6.1. Introducción

Cuando se ha escrito una aplicación con interfaz gráfica, resulta bastante sencillo convertirla en applet. Almacenada en un equipo A, esta puede descargarse mediante un navegador web desde un equipo B a través de Internet. De este modo, la aplicación inicial se comparte entre numerosos usuarios, y ahí radica el principal interés de convertir una aplicación en un applet. Sin embargo, no todas las aplicaciones pueden transformarse de esta manera: para no perjudicar al usuario que utiliza un applet en su navegador, el entorno del applet está regulado:

  • un applet no puede leer ni escribir en el disco del usuario
  • solo puede comunicarse con el ordenador desde el que ha sido descargada por el navegador.

Se trata de restricciones importantes. Implican, por ejemplo, que una aplicación que necesite leer información de un archivo o una base de datos deberá solicitarla a través de una aplicación intermediaria situada en el servidor desde el que se ha descargado.

La estructura general de una aplicación web sencilla es la siguiente:

Image

  • el cliente solicita un documento HTML al servidor web, normalmente mediante un navegador. Este documento puede contener un applet que funcionará como una aplicación gráfica autónoma dentro del documento HTML mostrado por el navegador del cliente.
  • Este applet podrá acceder a datos, pero solo a aquellos que se encuentren en el servidor web. No tendrá acceso ni a los recursos del equipo cliente en el que se ejecute ni a los de otros equipos de la red distintos de aquel desde el que se haya descargado.

5.6.2. La clase JApplet

5.6.2.1. Définition

Una aplicación puede descargarse mediante un navegador web si es una instancia de la clase java.applet.Applet o de la clase javax.swing.JApplet. Esta última deriva de la primera, que a su vez deriva de la clase Panel, la cual deriva de la clase Container. Una instancia de tipo Applet o JApplet, al ser de tipo container, podrá contener, por tanto, componentes (Component) tales como botones, casillas de selección, listas, etc. Veamos algunas indicaciones sobre la función de los distintos métodos:

Método
Función
public void destroy();
destruye la instancia del applet
public AppletContext getAppletContext();
obtiene el contexto de ejecución del applet (el documento HTML en el que se encuentra, otros applets del mismo documento, etc.)
public String getAppletInfo();
devuelve una cadena de caracteres que proporciona información sobre el applet
public AudioClip getAudioClip(URL url);
carga el archivo de audio especificado por URL
public AudioClip getAudioClip(URL url, String name);
carga el archivo de audio especificado por URL/name
public URL getCodeBase();
devuelve el URL del applet
public URL getDocumentBase();
devuelve el URL del documento HTML que contiene el applet
public Image getImage(URL url);
obtiene la imagen especificada por URL
public Image getImage(URL url, String name);
recoge la imagen especificada por URL/name
public String getParameter(String name);
obtiene el valor del parámetro name contenido en la etiqueta <applet> del documento HTML que contiene el applet.
public void init();
Este método lo inicia el navegador al ejecutar el applet por primera vez
public boolean isActive();
Estado del applet
public void play(URL url);
Reproduce el archivo de sonido especificado por URL
public void play(URL url, String name);
reproduce el archivo de sonido especificado por UR/name
public void resize(Dimension d);
establece el tamaño del marco del applet
public void resize(int width, int height);
lo mismo
public final void setStub(AppletStub stub);
 
public void showStatus(String msg);
muestra un mensaje en la barra de estado del applet
public void start();
el navegador ejecuta este método cada vez que se muestra el documento que contiene el applet
public void stop();
el navegador ejecuta este método cada vez que se abandona el documento que contiene el applet para pasar a otro (cambio de URL por parte del usuario)

La clase JApplet ha aportado algunas mejoras a la clase Applet, en particular la capacidad de contener componentes JMenuBar y c.a.d. de los menús, algo que no era posible con la clase Applet.

5.6.2.2. Ejecución de un applet: los métodos init, start y stop

Cuando un navegador carga un applet, invoca tres métodos del mismo:

init
Este método se invoca durante la carga inicial del applet. Por lo tanto, en él se incluirán las inicializaciones necesarias para la aplicación.
start
Este método se invoca cada vez que el documento que contiene el applet se convierte en el documento activo del navegador. Así, cuando un usuario carga un applet, los métodos init y start se ejecutarán en ese orden. Cuando salga del documento para visualizar otro, se ejecutará el método stop. Cuando vuelva a él más tarde, se ejecutará el método start.
stop
Este método se invoca cada vez que el usuario abandona el documento que contiene el applet.

Para muchos applets, solo es necesario el método init. Los métodos start y stop solo son necesarios si la aplicación inicia tareas (hilos) que se ejecutan en paralelo y de forma continua, a menudo sin que el usuario se dé cuenta. Cuando el usuario sale del documento, la parte visible de la aplicación desaparece, pero estas tareas en segundo plano siguen funcionando. A menudo, esto es innecesario. Por lo tanto, se aprovecha la llamada del navegador al método stop para detenerlas. Si el usuario vuelve al documento, se aprovecha la llamada del navegador al método start para reiniciarlas.

Tomemos, por ejemplo, un applet que tiene un reloj en su interfaz gráfica. Este reloj se mantiene mediante una tarea en segundo plano (hilo). Cuando el usuario sale de la página del applet en el navegador, no tiene sentido mantener el reloj, que ya se ha vuelto invisible: en el método «stop» del applet, se detendrá el hilo que gestiona el reloj. En el método «start», se volverá a iniciar para que, cuando el usuario vuelva a la página del applet, encuentre un reloj que marque la hora correcta.

5.6.3. Transformación de una aplicación gráfica en un applet

Suponemos aquí que esta transformación es posible, es decir, que cumple con las restricciones de ejecución de los applets. Una aplicación se inicia mediante el método main de una de sus clases:


public class application{
    
    public static main void(String arg[]){
        
    }
    
}// fin de categoría

Una aplicación de este tipo se convierte en un applet de la siguiente manera:

import java.applet.JApplet;

public class application extends JApplet{
    
    public void init(){
        
    }
    
}// fin de clase

Cabe destacar lo siguiente:

  1. la clase application deriva ahora de la clase JApplet
  2. el método main se sustituye por el método init.

A modo de ejemplo, volvemos a una aplicación ya estudiada: la gestión de listas.

Image

Los componentes de esta ventana son los siguientes:

n.º
tipo
nombre
función
1
JTextField
txtSaisie
campo de entrada
2
JList
jList1
lista contenida en un contenedor jScrollPane1
3
JList
jList2
lista contenida en un contenedor jScrollPane2
4
JButton
cmd1To2
transfiere los elementos seleccionados de la lista 1 a la lista 2
5
JButton
cmd2To1
hace lo contrario
6
JButton
cmdRaz1
vacía la lista 1
7
JButton
cmdRaz2
vacía la lista 2

La estructura de la aplicación era la siguiente:

public class interfaceAppli extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JLabel jLabel2 = new JLabel();
  JLabel jLabel3 = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmd1To2 = new JButton();
  JButton cmd2To1 = new JButton();
  DefaultListModel v1=new DefaultListModel();
  DefaultListModel v2=new DefaultListModel();
  JList jList1 = new JList(v1);
  JList jList2 = new JList(v2);
  JScrollPane jScrollPane1 = new JScrollPane();
  JScrollPane jScrollPane2 = new JScrollPane();
  JButton cmdRaz1 = new JButton();
  JButton cmdRaz2 = new JButton();
  JLabel jLabel4 = new JLabel();

   /**Crear el marco*/
  public interfaceAppli() {
    enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }//interfaceAppli

   /**Inicializar el componente*/
  private void jbInit() throws Exception  {
    ...
    txtSaisie.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(ActionEvent e) {
        txtSaisie_actionPerformed(e);
      }
    });
        ...
         // Jlist1 se coloca en el contenedor jScrollPane1
    jScrollPane1.getViewport().add(jList1, null);
        // Jlist2 se coloca en el contenedor jScrollPane2
    jScrollPane2.getViewport().add(jList2, null);
        ...
  }
   /**Sustituido, para que podamos salir cuando se cierre la ventana*/
  protected void processWindowEvent(WindowEvent e) {
...
  }

  void txtSaisie_actionPerformed(ActionEvent e) {
.............
}//clase
  void cmd1To2_actionPerformed(ActionEvent e) {
..........
  }//transferencia
  void cmdRaz1_actionPerformed(ActionEvent e) {
    // vaciar lista 1
    v1.removeAllElements();
  }//comando Raz1

  void cmdRaz2_actionPerformed(ActionEvent e) {
    // vaciar lista 2
    v2.removeAllElements();
  }///cmd Raz2

Para transformar la clase appli en applet, hay que proceder de la siguiente manera:

  • modificar la clase anterior interfaceAppli para que ya no derive de JFrame, sino de JApplet:
public class interfaceAppli extends JApplet {
  • eliminar la instrucción que establece el título de la ventana JFrame. Un applet JApplet no tiene barra de título
    // this.setTitle("JList");
  • Cambiar el constructor por un método init y, en dicho método, eliminar la gestión de eventos de ventana (WindowListener, ...). Un applet es un contenedor que no se puede redimensionar ni cerrar.
   /**Crear el marco*/
  public init() {
    // enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }//interfaceAppli
  • El método processWindowEvent debe eliminarse o comentarse
   /**Sustituido, para que podamos salir cuando se cierre la ventana*/
  //protected void processWindowEvent(WindowEvent e) {
  //  super.processWindowEvent(e);
  //  if (e.getID() == WindowEvent.WINDOW_CLOSING) {
  //    System.exit(0);
  //  }
  // }

A modo de ejemplo, se muestra el código completo del applet, excepto la parte generada automáticamente por JBuilder

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

public class interfaceAppli extends JApplet {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JLabel jLabel2 = new JLabel();
  JLabel jLabel3 = new JLabel();
  JTextField txtSaisie = new JTextField();
  JButton cmd1To2 = new JButton();
  JButton cmd2To1 = new JButton();
  DefaultListModel v1=new DefaultListModel();
  DefaultListModel v2=new DefaultListModel();
  JList jList1 = new JList(v1);
  JList jList2 = new JList(v2);
  JScrollPane jScrollPane1 = new JScrollPane();
  JScrollPane jScrollPane2 = new JScrollPane();
  JButton cmdRaz1 = new JButton();
  JButton cmdRaz2 = new JButton();
  JLabel jLabel4 = new JLabel();

   /**Crear el marco*/
  public void init() {
    // enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }//interfaceAppli

   /**Inicializar el componente*/
  private void jbInit() throws Exception  {
    //setIconImage(Toolkit.getDefaultToolkit().createImage(interfaceAppli.class.getResource("[Votre icône]")));
    contentPane = (JPanel) this.getContentPane();
    contentPane.setLayout(null);
    this.setSize(new Dimension(400, 308));
    // this.setTitle("JList");
    jScrollPane1.setBounds(new Rectangle(37, 133, 124, 101));
    jScrollPane2.setBounds(new Rectangle(232, 132, 124, 101));
.............
  }

  void txtSaisie_actionPerformed(ActionEvent e) {
     // el texto introducido se ha validado
     // se recupera sin los espacios iniciales y finales
    String texte=txtSaisie.getText().trim();
     // si está vacío, no lo queremos
    if(texte.equals("")){
      // mensaje de error
      JOptionPane.showMessageDialog(this,"Vous devez taper un texte",
        "Erreur",JOptionPane.WARNING_MESSAGE);
      // fin
      return;
    }//si
    // si no está vacío, se añade a los valores de la lista 1
    v1.addElement(texte);
     // y se vacía el campo de entrada
    txtSaisie.setText("");
  }

  void cmd1To2_actionPerformed(ActionEvent e) {
    // transferencia de los elementos seleccionados de la lista 1 a la lista 2
    transfert(jList1,jList2);
  }//cmd1To2

  private void transfert(JList L1, JList L2){
      // transferencia de los elementos seleccionados de la lista 1 a la lista 2
       // se recupera la tabla de índices de los elementos seleccionados en L1
      int[] indices=L1.getSelectedIndices();
      // ¿Hay que hacer algo?
      if (indices.length==0) return;
      // se recuperan los valores de L1
      DefaultListModel v1=(DefaultListModel)L1.getModel();
      // y los de L2
      DefaultListModel v2=(DefaultListModel)L2.getModel();
      for(int i=indices.length-1;i>=0;i--){
        // se añaden a L2 los valores seleccionados en L1
        v2.addElement(v1.elementAt(indices[i]));
        // los elementos de L1 copiados en L2 deben eliminarse de L1
        v1.removeElementAt(indices[i]);
      }//para
  }//transferencia

  private void affiche(String message){
    // muestra un mensaje
      JOptionPane.showMessageDialog(this,message,
        "Suivi",JOptionPane.INFORMATION_MESSAGE);
  }// muestra

  void cmd2To1_actionPerformed(ActionEvent e) {
    // transfiere los elementos seleccionados de jList2 a jList1
      transfert(jList2,jList1);
  }//cmd2TO1

  void cmdRaz1_actionPerformed(ActionEvent e) {
    // vaciar la lista 1
    v1.removeAllElements();
  }//comando Raz1

  void cmdRaz2_actionPerformed(ActionEvent e) {
    // lista vacía 2
    v2.removeAllElements();
  }//comando Borrar2

}//clase

Se puede compilar el código fuente de este applet. Aquí lo hacemos con el JDK, sin el JBuilder.

E:\data\serge\Jbuilder\interfaces\JApplets\1>javac.bat interfaceAppli.java

E:\data\serge\Jbuilder\interfaces\JApplets\1>dir
12/06/2002  16:40                6 148 interfaceAppli.java
12/06/2002  16:41                  527 interfaceAppli$1.class
12/06/2002  16:41                  525 interfaceAppli$2.class
12/06/2002  16:41                  525 interfaceAppli$3.class
12/06/2002  16:41                  525 interfaceAppli$4.class
12/06/2002  16:41                  525 interfaceAppli$5.class
12/06/2002  16:41                4 759 interfaceAppli.class

La aplicación se puede probar con el programa AppletViewer de JDK, que permite ejecutar applets o un navegador. Para ello, hay que crear el documento HTML appli.htm, que contendrá el applet:

<html>
    <head>
        <title>listes swing</title>
    </head>
    <body>
        <applet 
        code="interfaceAppli.class"
      width="400"
      height="300">
        </applet>
    </body>
</html>

Se trata de un documento HTML clásico, salvo por la presencia de la etiqueta «applet». Esta se ha utilizado con tres parámetros:

code="interfaceAppli.class"
nombre de la clase JAVA compilada que hay que cargar para ejecutar el applet
width="400"
ancho del marco del applet en el documento
height="300"
altura del marco del applet en el documento

Una vez creado el archivo appli.htm, se puede cargar con el programa appletviewer de JDK o con un navegador. En una ventana de DOS, se escribe el siguiente comando en la carpeta del archivo appli.htm:

appletviewer appli.htm

Se supone aquí que el directorio del ejecutable appletviewer.exe se encuentra en el directorio PATH de la ventana de DOS; de lo contrario, habría que indicar la ruta completa del ejecutable appletviewer.exe. Aparecerá la siguiente ventana:

Image

Detendremos la ejecución del applet con la opción Applet/Quitter. Probemos ahora el applet con un navegador. Este debe utilizar una máquina virtual Java 2 para poder mostrar los componentes Swing. Hasta hace poco (2001), esta restricción suponía un problema, ya que Sun producía JDK que los fabricantes de navegadores no incorporaban hasta mucho más tarde. Finalmente, Sun puso fin a este obstáculo poniendo a disposición de los usuarios una aplicación denominada «Java plugin», que permite a los navegadores Internet Explorer y Netscape Navigator utilizar las últimas versiones de JRE producidas por Sun (JRE = Java Runtime Environment). Las pruebas que se describen a continuación se han realizado con IE 5.5, equipado con el plugin Java 1.4.

Una primera forma de probar el applet interfaceAppli.class es cargar el archivo HTML appli.htm directamente en el navegador haciendo doble clic sobre él. El navegador mostrará entonces la página HTML y su applet sin necesidad de un servidor web:

Image

Según el URL (1), se observa que el archivo se ha cargado directamente en el navegador. En el siguiente ejemplo, el archivo appli.htm se ha solicitado a un servidor web Apache que funciona en el puerto 81 del equipo local:

Image

5.6.4. La opción de formato <applet> en un documento HTML

Hemos visto que, en un documento HTML, el applet se referenciaba mediante la etiqueta de formato <applet>. Esta etiqueta puede tener varios parámetros:

<applet
    code=
    width=
    height=
    codebase=
    align=
    hspace=
    vspace=
alt=
>
    <param name1=nom1 value=val1>
    <param name2=nom2 value=val2>
    texte
>
</applet>

El significado de los parámetros es el siguiente:

Parámetro
Significado
code
obligatorio: nombre del archivo .class que se va a ejecutar
width
obligatorio: ancho del applet en el documento
height
obligatorio: altura…
codebase
opcional: URL del directorio que contiene el applet que se va a ejecutar. Si no se especifica «codebase», el applet se buscará en el directorio del documento HTML que lo referencia
align
opcional: posición (LEFT, RiGHT, CENTER) del applet en el documento
hspace
opcional: margen horizontal que rodea el applet, expresado en píxeles
vspace
opcional: margen vertical que rodea el applet, expresado en píxeles
alt
opcional: texto que se muestra en lugar del applet si el navegador no puede cargarlo
param
opcional: parámetro pasado al applet que especifica su nombre (name) y su valor (value). El applet podrá recuperar el valor del parámetro «nombre1» mediante «val1=getParameter("nombre1")»
Se pueden utilizar tantos parámetros como se desee
texte
Opcional: se mostrará en cualquier navegador que no pueda ejecutar un applet, por ejemplo, porque no dispone de una máquina virtual Java.

Veamos un ejemplo para ilustrar el paso de parámetros a un applet:

<html>
    <head>
        <title>paramètres applet</title>
    </head>
    <body>
        <applet code="interfaceParams.class" width="400" height="300">
      <param name="param1" value="val1">
      <param name="param2" value="val2">      
        </applet>
    </body>
</html>

El código Java del applet interfaceParams se ha generado tal y como se ha indicado anteriormente. Se ha creado una aplicación JBuilder y, a continuación, se han realizado las transformaciones mencionadas anteriormente:

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

public class interfaceParams extends JApplet {
  JPanel contentPane;
  JScrollPane jScrollPane1 = new JScrollPane();
  JLabel jLabel1 = new JLabel();
  DefaultListModel params=new DefaultListModel();
  JList lstParams = new JList(params);

  //Construir el marco
  public void init() {
    // enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // otras inicializaciones
    moreInit();
  }

   // inicializaciones
  public void moreInit(){
     // muestra los valores de los parámetros del applet
    params.addElement("nom=param1 valeur="+getParameter("param1"));
    params.addElement("nom=param2 valeur="+getParameter("param2"));
  }//más inicializaciones

   //Inicializar el componente
  private void jbInit() throws Exception  {
............
    this.setSize(new Dimension(205, 156));
    //this.setTitle("Parámetros de un applet");
    jScrollPane1.setBounds(new Rectangle(19, 53, 160, 73));
............
 }
}

La ejecución con AppletViewer:

Image

5.6.5. Acceder a recursos remotos desde un applet

Muchas aplicaciones necesitan utilizar información contenida en archivos o bases de datos. Ya hemos señalado que un applet no tiene acceso a los recursos del equipo en el que se ejecuta. Se trata de una medida de sentido común. De lo contrario, bastaría con escribir un applet para «espiar» el disco de quienes lo cargan. No obstante, el applet sí tiene acceso a los recursos del servidor desde el que se ha descargado, por ejemplo, a los archivos. Esto es lo que vamos a ver ahora.

5.6.5.1. La clase URL

Cualquier aplicación Java puede leer un archivo presente en un equipo de la red gracias a la clase java.net.URL (URL = Uniform Resource Locator). Un URL identifica un recurso de la red, el equipo en el que se encuentra, así como el protocolo y el puerto de comunicación que hay que utilizar para recuperarlo, con el siguiente formato:

protocolo:puerto//ordenador/archivo

Así, el URL http://www.ibm.com/index.html hace referencia al archivo index.html de la máquina www.ibm.com. Se puede acceder a él mediante el protocolo http. No se especifica el puerto: el navegador utilizará el puerto 80, que es el puerto predeterminado del servicio http.

Veamos con más detalle algunos de los elementos de la clase URL:

public URL(String spec)
crea una instancia de URL a partir de una cadena del tipo «protocolo:puerto//máquina/archivo»; lanza una excepción si la sintaxis de la cadena no se corresponde con una URL
  
public String getFile()
permite obtener el campo «archivo» de la cadena «protocolo:puerto//máquina/archivo» de URL
  
public String getHost()
permite obtener el campo «máquina» de la cadena «protocolo:puerto//máquina/archivo» del URL
  
public String getPort()
permite obtener el campo «puerto» de la cadena «protocolo:puerto//máquina/archivo» del URL
  
public String getProtocol()
permite obtener el campo «protocolo» de la cadena «protocolo:puerto//máquina/archivo» del URL
  
public URLConnection openConnection()
abre la conexión con el equipo remoto getHost() en su puerto getPort() según el protocolo getProtocol() con el fin de leer el archivo getFile(). Lanza una excepción si no se ha podido establecer la conexión
  
public final InputStream openStream()
Abreviatura de openConnection().getInputStream(). Permite obtener un flujo de entrada a partir del cual se podrá leer el contenido del archivo getFile(). Lanza una excepción si no se ha podido obtener el flujo
  
public String toString()
muestra la identidad de la instancia de URL

Aquí hay dos métodos que nos interesan:

  • public URL(String spec) para especificar el archivo que se va a procesar
  • public final InputStream openStream() para procesarlo

5.6.5.2. Un ejemplo de consola

Vamos a escribir un programa en Java, sin interfaz gráfica, encargado de mostrar en pantalla el contenido de un URL que se le pasa como parámetro. Una llamada de ejemplo podría ser la siguiente:

    DOS> java urlcontenu http://istia.univ-angers.fr/index.html

El programa es relativamente sencillo:


import java.net.*;             // para la clase URL
import java.io.*;                // para los flujos (stream)

public class urlcontenu{

// muestra el contenido de URL pasado como argumento
// este contenido debe ser texto para que sea legible

  public static void main (String arg[]){
    // comprobación de los argumentos
    if(arg.length==0){
      System.err.println("Syntaxe pg url");
      System.exit(0);
    }
    
    try{
      // creación de URL      
      URL url=new URL(arg[0]);
      // lectura del contenido
      try{
        // creación del flujo de entrada
        BufferedReader is=new BufferedReader(new InputStreamReader(url.openStream()));
        try{
          // lectura de las líneas de texto en el flujo de entrada
          String ligne;
          while((ligne=is.readLine())!=null)
            System.out.println(ligne);
        } catch (Exception e){
            System.err.println("Erreur lecture : " +e);
        }
        finally{
          try { is.close(); } catch (Exception e) {}
        }
      } catch (Exception e){
          System.err.println("Erreur openStream : " +e);
      }
    } catch (Exception e){
      System.err.println("Erreur création URL : " +e);
    }
  }// fin de main
}// fin de clase

Tras comprobar que efectivamente hay un argumento, se intenta crear un URL con dicho argumento:


// creación de URL      
      URL url=new URL(arg[0]);

Esta creación puede fallar si el argumento no respeta la sintaxis de los URL protocole:port//machine/fichier. Si se dispone de un URL sintácticamente correcto, se intenta crear un flujo de entrada a partir del cual se puedan leer líneas de texto. El flujo de entrada proporcionado por una función URL URL.openStream() proporciona un flujo de tipo InputStream, que es un flujo orientado a caracteres: la lectura se realiza carácter por carácter y hay que formar las líneas uno mismo. Para poder leer líneas de texto, hay que utilizar el método readLine de las clases BufferedReader o DataInputStream.On; en este caso se ha elegido BufferedReader. Solo nos queda transformar el flujo InputStream de URL en el flujo BufferedReader. Esto se hace creando un flujo intermedio InputStreamReader:


BufferedReader is=new BufferedReader(new InputStreamReader(url.openStream()));

La creación de este flujo puede fallar. Se gestiona la excepción correspondiente. Una vez obtenido el flujo, solo nos queda leer las líneas de texto que contiene y mostrarlas en pantalla.


            String ligne;
         while((ligne=is.readLine())!=null)
                System.out.println(ligne);

Una vez más, la lectura puede provocar una excepción que debe gestionarse. A continuación se muestra un ejemplo de ejecución del programa que requiere una URL local:

E:\data\serge\Jbuilder\interfaces\JApplets\3>java urlcontenu http://localhost
<html>

<head>


<title>Bienvenue dans le Serveur Web personnel</title>
</head>

<body bgcolor="#FFFFFF" link="#0080FF" vlink="#0080FF"
topmargin="0" leftmargin="0">

<table border="0" cellpadding="0" cellspacing="0" width="100%"
bgcolor="#000000">
............
                </table>
                </center></div></td>
            </tr>
        </table>
        </td>
    </tr>
</table>
</body>
</html>

5.6.5.3. Un ejemplo gráfico

La interfaz gráfica será la siguiente:

Image

Número
Nombre
Tipo
Función
1
txtURL
JTextField
URL: a leer
2
btnCharger
JButton
Botón para iniciar la reproducción de URL
3
JScrollPane1
JScrollPane
panel deslizante
4
lstURL
JList
lista que muestra el contenido del URL solicitado

Al ejecutarlo, obtenemos un resultado similar al del programa de consola:

Image

El código relevante de la aplicación es el siguiente:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.net.*;
import java.io.*;

public class interfaceURL extends JFrame {
  JPanel contentPane;
  JLabel jLabel1 = new JLabel();
  JTextField txtURL = new JTextField();
  JButton btnCharger = new JButton();
  JScrollPane jScrollPane1 = new JScrollPane();
  DefaultListModel lignes=new DefaultListModel();
  JList lstURL = new JList(lignes);

  //Crear el marco
  public interfaceURL() {
..............
  }

   //Inicializar el componente
  private void jbInit() throws Exception  {
..............
  }

   //Sustituido, para que podamos salir cuando se cierre la ventana
  protected void processWindowEvent(WindowEvent e) {
...................
  }

  void txtURL_caretUpdate(CaretEvent e) {
    // establece el estado del botón «Cargar»
    btnCharger.setEnabled(! txtURL.getText().trim().equals(""));
  }

  void btnCharger_actionPerformed(ActionEvent e) {
    // muestra el contenido de URL en la lista
    try{
      afficherURL(txtURL.getText().trim());
    }catch(Exception ex){
       // muestra un error
      JOptionPane.showMessageDialog(this,"Erreur : " + ex.getMessage(),"Erreur",JOptionPane.ERROR_MESSAGE);
    }//try-catch
  }

  private void afficherURL(String strURL) throws Exception {
    // muestra el contenido de URL y strURL en la lista
     // No se gestiona ninguna excepción de forma específica. Simplemente se reenvían

     // creación del URL
    URL url=new URL(strURL);
     // creación del flujo de entrada
    BufferedReader IN=new BufferedReader(new InputStreamReader(url.openStream()));
    // lectura de las líneas de texto en el flujo de entrada
    String ligne;
    while((ligne=IN.readLine())!=null)
      lignes.addElement(ligne);
     // cierre del flujo de lectura
    IN.close();
  }
}

5.6.5.4. Un applet

La aplicación gráfica anterior se transforma en un applet, tal y como se ha mostrado en varias ocasiones. El applet se inserta en un documento HTML appliURL.htm:

<html>
  <head>
    <title>Applet lisant une URL</title>
  </head>
  <body>
    <h1>Applet lisant une URL</h1>
    <applet
      code="appletURL.class"
      width="400"
      height="300"
    >
    </applet>
  </body>
</html>

Image

En el ejemplo anterior, el navegador solicitó el archivo «URL» http://localhost:81/Japplets/2/appliURL.htm a un servidor web «apache» que funciona en el puerto 81. A continuación, el applet se mostró en el navegador. En ella, se volvió a solicitar el archivo URL http://localhost:81/Japplets/2/appliURL.htm para comprobar que se obtenía efectivamente el archivo appliURL.htm que habíamos creado. Ahora intentemos cargar un archivo URL que no pertenezca al equipo que ha generado el applet (en este caso, localhost):

Image

Se ha rechazado la carga del archivo URL. Aquí nos encontramos de nuevo con la restricción propia de los applets: solo pueden acceder a los recursos de red del equipo desde el que se han descargado. Existe una forma de que el applet eluda esta restricción, que consiste en delegar las solicitudes de red a un programa servidor ubicado en el equipo desde el que se ha descargado. Este programa realizará las solicitudes de red en lugar del applet y le enviará los resultados. A esto se le denomina programa relé.

5.7. El applet IMPOTS

Ahora vamos a convertir la aplicación gráfica IMPOTS en un applet. Esto tiene cierto interés: la aplicación estará disponible para cualquier usuario de Internet que disponga de un navegador y del plugin de Java 1.4 (gracias al componente JSpinner). De este modo, nuestra aplicación se vuelve global... Esta modificación requerirá algo más que la simple transformación de aplicación gráfica a applet, que ya se ha convertido en algo habitual. La aplicación utiliza una opción de menú Initialiser cuyo objetivo es leer el contenido de un archivo local. Sin embargo, si nos imaginamos una aplicación web, vemos que este esquema ya no es válido:

Image

Es evidente que los datos que definen las escalas impositivas estarán en el servidor web y no en cada uno de los equipos cliente. El archivo que hay que leer en el servidor web se colocará aquí en la misma carpeta que el applet, y la opción Initialiser deberá ir a buscarlo allí. Los ejemplos anteriores han mostrado cómo hacerlo. Por otra parte, para seleccionar el archivo de datos localmente, se había utilizado un componente JFileChooser que ya no tiene sentido aquí.

El documento HTML que contiene el applet será el siguiente:

<html>
  <head>
    <title>Applet de calcul d'impôts</title>
  </head>
  <body>
    <h2>Calcul d'impôts</h2>
    <applet
      code="appletImpots.class"
      width="320"
      height="270"
    >

<param name="data" value="impots.txt">


    </applet>
  </body>
</html>

Cabe destacar el parámetro data, cuyo valor es el nombre del archivo que contiene los datos de la escala impositiva. El documento HTML, la clase appletImpots, la clase impots y el archivo impots.txt se encuentran todos en la misma carpeta del servidor web:

E:\data\serge\web\JApplets\impots>dir
12/06/2002  13:24                  247 impots.txt
13/06/2002  10:17                  654 appletImpots$2.class
13/06/2002  10:17                  653 appletImpots$3.class
13/06/2002  10:17                  653 appletImpots$4.class
13/06/2002  10:17                  651 appletImpots$5.class
13/06/2002  10:06                  651 appletImpots$6.class
13/06/2002  10:17                8 655 appletImpots.class
13/06/2002  10:17                  657 appletImpots$1.class
13/06/2002  10:24                  286 appletImpots.htm
13/06/2002  10:17                1 305 impots.class

El código del applet es el siguiente (solo hemos resaltado los cambios respecto a la aplicación gráfica):

...........
import java.net.*;
import java.applet.Applet;

public class appletImpots extends JApplet {
  // los componentes de la ventana
  JPanel contentPane;
............................

   // los atributos de la clase
  double[] limites=null;
  double[] coeffr=null;
  double[] coeffn=null;
  impots objImpots=null;
  String urlDATA=null;

  //Crear el marco
  public void init() {
    //enableEvents(AWTEvent.WINDOW_EVENT_MASK);
    try {
      jbInit();
    }
    catch(Exception e) {
      e.printStackTrace();
    }
     // otras inicializaciones
    moreInit();
  }

   // inicialización del formulario
  private void moreInit(){
     // menú «Calcular» desactivado
    mnuCalculer.setEnabled(false);
................
     // se recupera el nombre del archivo de la tabla de impuestos
    String nomFichier=getParameter("data");
    // ¿Error?
    if(nomFichier==null){
       // mensaje de error
      txtStatus.setText("Le paramètre data de l'applet n'a pas été initialisé");
       // Se bloquea la opción «Inicializar»
      mnuInitialiser.setEnabled(false);
      // fin
      return;
    }//si
     // se establece el URL de los datos
    urlDATA=getCodeBase()+"/"+nomFichier;
  }//moreInit

   //Inicializar el componente
  private void jbInit() throws Exception  {
...................
  }

  void mnuQuitter_actionPerformed(ActionEvent e) {
    // Se sale de la aplicación
    System.exit(0);
  }

  void mnuInitialiser_actionPerformed(ActionEvent e) {
    // se carga el archivo de datos
    try{
       // lectura de datos
      lireDATA();
       // creación del objeto «impuestos»
      objImpots=new impots(limites,coeffr,coeffn);
................
  }

  private void lireDATA() throws Exception {
     // las tablas de datos
    ArrayList aLimites=new ArrayList();
    ArrayList aCoeffR=new ArrayList();
    ArrayList aCoeffN=new ArrayList();
    String[] champs=null;

     // apertura del archivo en modo lectura
    BufferedReader IN=new BufferedReader(new InputStreamReader(new URL(urlDATA).openStream()));
    // se lee el archivo línea por línea
....................
  }

  void mnuCalculer_actionPerformed(ActionEvent e) {
    // cálculo del impuesto
......................
  }

  void txtSalaire_caretUpdate(CaretEvent e) {
..........
  }

  void mnuEffacer_actionPerformed(ActionEvent e) {
...............
  }
}

Aquí solo comentamos los cambios derivados del hecho de que el archivo de datos que se va a leer se encuentra ahora en un equipo remoto en lugar de en un equipo local:

El nombre del archivo de datos que se va a leer, URL, se obtiene mediante el siguiente código:

     // se recupera el nombre del archivo de la tabla de impuestos
    String nomFichier=getParameter("data");
    // ¿Error?
    if(nomFichier==null){
       // mensaje de error
      txtStatus.setText("Le paramètre data de l'applet n'a pas été initialisé");
       // se desactiva la opción «Inicializar»
      mnuInitialiser.setEnabled(false);
      // fin
      return;
    }//si
     // se establece el URL de los datos
    urlDATA=getCodeBase()+"/"+nomFichier;

Recordemos que el nombre del archivo «impots.txt» se ha pasado al parámetro data del applet:

    <param name="data" value="impots.txt">

Por lo tanto, el código anterior comienza recuperando el valor del parámetro data y gestionando cualquier posible error. Si el valor de URL del documento HTML que contiene el applet es http://localhost:81/JApplets/impots/appletImpots.htm, el URL del archivo impots.txt será http://localhost:81/JApplets/impots/impots.txt. Tenemos que construir el nombre de este URL. El método getCodeBase() del applet proporciona el URL de la carpeta de donde se ha recuperado el documento HTML que contiene el applet, por lo que, en nuestro ejemplo, http://localhost:81/JApplets/impots. La siguiente instrucción permite, por tanto, construir el URL del archivo de datos:

    urlDATA=getCodeBase()+"/"+nomFichier;

En el método lireFichier(), que lee el contenido de URL y urlData, se encuentra la creación del flujo de entrada que permitirá leer los datos:

     // apertura del archivo en modo lectura
    BufferedReader IN=new BufferedReader(new InputStreamReader(new URL(urlDATA).openStream()));

A partir de ahí, ya no se puede distinguir si los datos proceden de un archivo remoto o local. A continuación se muestra un ejemplo de ejecución del applet:

Image

5.8. Conclusión

En este capítulo se ha presentado

  • una introducción a la creación de interfaces gráficas con JBuilder
  • los componentes Swing más habituales
  • la creación de applets

Recordaremos que

  • el código generado por JBuilder se puede escribir a mano. Una vez obtenido este código de una forma u otra, basta con un simple JDK para ejecutarlo y JBuilder ya no es imprescindible.
  • el uso de una herramienta como JBuilder puede suponer importantes ganancias de productividad:
    • aunque sea posible escribir a mano el código generado por JBuilder, esto puede llevar mucho tiempo y tiene poco interés, ya que la lógica de la aplicación suele estar en otro lugar.
    • El código generado puede resultar instructivo. Leerlo y estudiarlo es una buena forma de descubrir ciertos métodos y propiedades de los componentes que se utilizan por primera vez.
    • JBuilder es multiplataforma. Por lo tanto, se conserva lo aprendido al pasar de una plataforma a otra. Poder escribir programas que funcionen tanto en Windows como en Linux es, por supuesto, un factor de productividad muy importante. Pero esto se debe a Java en sí mismo y no a JBuilder.

5.9. JBuilder en Linux

Todos los ejemplos anteriores se han probado en Windows 98. Cabe preguntarse si los programas escritos son portables tal cual a Linux. Siempre que el equipo con Linux en cuestión disponga de las clases utilizadas por los distintos programas, sí lo son. Si, por ejemplo, ha instalado JBuilder en Linux, las clases necesarias ya se encuentran en su equipo. A continuación se muestra, a modo de ejemplo, el resultado de la ejecución de uno de nuestros programas en el componente JList con JBuilder 4 en Linux:

A continuación, te explicamos cómo instalar JBuilder 4 Foundation en un equipo con Linux. Su instalación en un equipo con Win9x no plantea ningún problema y es similar al proceso que vamos a describir a continuación. Es probable que la instalación de versiones posteriores sea diferente, pero este documento puede ofrecerte, no obstante, alguna información útil.

JBuilder está disponible en la página web de Inprise en la URL http://www.inprise.com/jbuilder

Image

En esta página se encuentran los enlaces para Windows y Linux, entre otros. A continuación describimos la instalación de JBuilder en un equipo Linux que cuenta con la interfaz gráfica KDE.

Al seguir el enlace «Jbuilder 4 for Linux», aparece un formulario. Lo rellenamos y, al final, obtenemos dos archivos:

jb4docs_fr.tar.gz: la documentación y los ejemplos de JBuilder 4

jb4fndlinux_fr.tar.gz: JBuilder 4 Foundation. Se trata de una versión limitada del JBuilder 4 comercial, pero suficiente en un contexto educativo.

Se te enviará por correo electrónico una clave de activación del software. Esta clave te permitirá utilizar JBuilder 4 y se te solicitará la primera vez que lo utilices. Si pierdes esta clave, puedes volver a la URL anterior y seguir el enlace «get your activation key». A partir de ahora, daremos por hecho que eres root y que te encuentras en el directorio de los dos archivos tar.gz mencionados anteriormente. Descomprime los dos archivos:

[tar xvzf jb4fndlinux_fr.tar.gz]
[tar xvzf jb4docs_fr.tar.gz]
[ls -l]
drwxr-xr-x    3 nobody   nobody       4096 oct 10  2000 docs
drwxr-xr-x    3 nobody   nobody       4096 déc  5 13:00 foundation
[ls -l foundation]
-rw-r--r--    1 nobody   nobody       1128 déc  5 13:00 deploy.txt
-rwxr-xr-x    1 nobody   nobody   69035365 déc  5 13:00 fnd_linux_install.bin
drwxr-xr-x    2 nobody   nobody       4096 déc  5 13:00 images
-rw-r--r--    1 nobody   nobody      15114 déc  5 13:00 index.html
-rw-r--r--    1 nobody   nobody      23779 déc  5 13:00 license.txt
-rw-r--r--    1 nobody   nobody      75739 déc  5 13:00 release_notes.html
-rw-r--r--    1 nobody   nobody      31902 déc  5 13:00 whatsnew.html
[ls -l docs]
-rw-r--r--    1 nobody   nobody       1128 oct 10  2000 deploy.txt
-rwxr-xr-x    1 nobody   nobody   40497874 oct 10  2000 doc_install.bin
drwxr-xr-x    2 nobody   nobody       4096 oct 10  2000 images
-rw-r--r--    1 nobody   nobody       9210 oct 10  2000 index.html
-rw-r--r--    1 nobody   nobody      23779 oct 10  2000 license.txt
-rw-r--r--    1 nobody   nobody      75739 oct 10  2000 release_notes.html
-rw-r--r--    1 nobody   nobody      31902 oct 10  2000 whatsnew.html

En los dos directorios generados, el archivo .bin es el archivo de instalación. Además, abre con un navegador el archivo index.html de cada uno de los directorios. En ellos se indican los pasos a seguir para instalar ambos productos. Empecemos por instalar JBuilder Foundation:

[cd foundation]
[./fnd_linux_install.bin]

Aparece la primera pantalla de la instalación. A continuación aparecerán muchas más:

Image

Confirma. A continuación aparece una pantalla con explicaciones:

Image

Ejecute [suivant].

Image

Acepte el contrato de licencia y ejecute [suivant].

Image

Acepta la ubicación propuesta para JBuilder (anótala, la necesitarás más adelante) y ejecuta [suivant]. La instalación se realiza rápidamente.

Image

Ya estamos listos para una primera prueba. En KDE, abre el gestor de archivos para acceder al directorio de los ejecutables de JBuilder: /opt/jbuilder4/bin (si has instalado JBuilder en /opt/jbuilder4):

Image

Haz clic en el icono jbuilder que aparece arriba. Como es la primera vez que utilizas JBuilder, tendrás que introducir tu clave de activación:

Image

Rellene los campos Nom y Société. Pulsa [Ajouter] para introducir tu clave de activación. Recuerda que esta clave te la deben haber enviado por correo electrónico y que, si la has perdido, puedes recuperarla en la URL desde donde descargaste JBuilder 4 (véase el inicio de la instalación). Una vez aceptada tu clave de activación, se te recordarán las condiciones de la licencia:

Image

Al introducir OK, volverá a la ventana de introducción de datos que ya ha visto:

Image

Introduzca OK para finalizar esta fase, que solo se ejecuta la primera vez. A continuación, aparecerá el entorno de desarrollo de JBuilder:

Image

Sal de JBuilder para instalar ahora la documentación. Esta instalará una serie de archivos que se utilizarán en la ayuda de JBuilder, ayuda que, por el momento, está incompleta. Vuelve al directorio donde has descomprimido los archivos tar.gz de JBuilder. El programa de instalación se encuentra en el directorio docs. Accede a él:

[cd docs]
[ls -l]
-rw-r--r--    1 nobody   nobody       1128 oct 10  2000 deploy.txt
-rwxr-xr-x    1 nobody   nobody   40497874 oct 10  2000 doc_install.bin
drwxr-xr-x    2 nobody   nobody       4096 oct 10  2000 images
-rw-r--r--    1 nobody   nobody       9210 oct 10  2000 index.html
-rw-r--r--    1 nobody   nobody      23779 oct 10  2000 license.txt
-rw-r--r--    1 nobody   nobody      75739 oct 10  2000 release_notes.html
-rw-r--r--    1 nobody   nobody      31902 oct 10  2000 whatsnew.html

El archivo de instalación es doc_install.bin, pero no se puede ejecutar inmediatamente. Si lo haces, la instalación fallará sin que se entienda por qué. Lee el archivo index.html con un navegador para obtener una descripción detallada del proceso de instalación. El instalador necesita una máquina virtual Java, concretamente un programa llamado java, que debe encontrarse en el directorio PATH de tu equipo. Recordamos que PATH es una variable de Unix cuyo valor, con el formato rep1:rep2:...:repn, indica los directorios repi que deben explorarse en la búsqueda de un ejecutable. En este caso, el instalador solicita la ejecución de un programa java. Puede que tengas varias versiones de este programa, dependiendo del número de máquinas virtuales Java que hayas instalado aquí o allá. Como es lógico, utilizaremos la que incluye JBuilder 4 y que se encuentra en /opt/jbuilder4/jdk1.3/bin. Para añadir este directorio a la variable PATH:

[echo $PATH]
/usr/local/sbin:/usr/sbin:/sbin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin
[export PATH=/opt/jbuilder4/jdk1.3/bin/:$PATH]
[echo $PATH]
/opt/jbuilder4/jdk1.3/bin/:/usr/local/sbin:/usr/sbin:/sbin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin

Ahora podemos iniciar la instalación de la documentación:

[./doc_install.bin]
Preparing to install...

Se va a realizar una instalación gráfica:

Image

Es muy similar a la de JBuilder Foundation. Por lo tanto, no la detallaremos. Hay una pantalla que no hay que pasarse por alto:

Image

Normalmente, el instalador debería detectar automáticamente dónde has instalado JBuilder Foundation. Por lo tanto, no modifiques lo que te propone, salvo, por supuesto, que sea incorrecto.

Una vez instalada la documentación, estamos listos para realizar una nueva prueba de JBuilder. Ejecuta la aplicación tal y como se ha mostrado anteriormente. Una vez que JBuilder esté en marcha, selecciona la opción Ayuda/Tema de ayuda. En el panel de la izquierda, haz clic en el enlace Tutoriels:

Image

JBuilder incluye un conjunto de tutoriales que te permitirán iniciarte en la programación en Java. A continuación hemos seguido el tutorial «Creación de una aplicación» mencionado anteriormente.

Image

En JBuilder, selecciona Archivo/Nuevo proyecto. Un asistente te mostrará tres pantallas:

Image

Vamos a crear una ventana sencilla con el título «Hola a todos». Llamaremos a este proyecto coucou. El ejemplo se ha ejecutado mediante root. A continuación, JBuilder propone agrupar todos los proyectos en un directorio jbproject dentro del directorio de conexión de root. El proyecto coucou se colocará en el directorio coucou (campo «Nombre del directorio del proyecto») de jbproject. Cree [suivant].

Image

Esta segunda pantalla es un resumen de las diferentes rutas de su proyecto. No hay nada que modificar. Seleccione [suivant].

Image

La tercera pantalla le pide que personalice su proyecto. Rellénela y pulse [Terminer].

Ahora ejecute Fichier/Nouveau:

Image

Selecciona Application y ejecuta OK. Un nuevo asistente te mostrará dos pantallas:

Image

El campo paquet recoge el nombre del proyecto. El campo «clase» solicita el nom que se asignará a la clase principal del proyecto. Tome el nombre anterior y introduzca [suivant]:

Image

Nuestra aplicación incluye una segunda clase Java para la ventana de la aplicación. Asigna un nombre a esta clase. El campo Titre es el título que deseas asignar a esta ventana. El cuadro options te ofrece opciones para tu ventana. Selecciónalas todas y establece [Terminer]. A continuación, volverás al entorno JBuilder, que se habrá actualizado con la información que has proporcionado para tu proyecto:

Image

En la ventana de la parte superior izquierda (1) aparece la lista de archivos que componen su proyecto. Entre ellos se encuentran las dos clases .java a las que acabamos de asignar un nombre. En la ventana de la derecha (2) aparece el código Java de la clase coucouCadre. En la ventana de la izquierda/parte inferior, aparece la estructura (clases, métodos, atributos) de su proyecto. Está listo para ejecutarse. Pulse el botón 4 de arriba, seleccione Ejecutar/Ejecutar el proyecto o seleccione F9. Debería aparecer la siguiente ventana:

Image

Al hacer clic en la opción Aide, deberías encontrar la información que has proporcionado a los asistentes de creación. No vamos a seguir adelante. Ahora es el momento de sumergirte en los tutoriales.

Antes de terminar, veamos simplemente que puede utilizar, no JBuilder y su interfaz gráfica, sino el JDK que este ha traído consigo y que ha colocado en /opt/jbuilder4/jdk1.3. Compile el siguiente archivo essai1.java:

import java.io.*;

public class essai1{
    public static void main(String args[]){
        System.out.println("coucou");
        System.exit(0);
    }
}

Compilémoslo y ejecutémoslo con el JDK de JBuilder:

[ls -l *.java]
-rw-r--r--    1 root     root          135 mai  9 21:57 essai1.java
[/opt/jbuilder4/jdk1.3/bin/javac essai1.java]
[/opt/jbuilder4/jdk1.3/bin/java essai1]
coucou