Topicos GUI Avanzados H. Tejeda Mayo 2016 Índice 1. Panel de contenido 1 2. Clase Color 4 3. Manejadores de diseño 5 4. JPanel 12 5. JScrollPane 16 6. Eventos y su manejo 18 7. Métodos de la clase AWTEvent 23 8. Manejo de eventos del ratón 23 9. Uso de menús 28 1. Panel de contenido La clase JFrame es una clase Java Swing contenedora de nivel superior. Las otras dos clases contenedoras de nivel superior son JDialog y JApplet. Cada componente GUI (Graphical User Interface) que aparece en la pantalla deberá ser parte de una jerarquı́a de contención. Una jerarquı́a de contención es un árbol de componentes que tienen un contenedor de nivel superior como su raı́z. Cada contenedor de nivel superior tiene un panel de contenido que contiene todos los componentes visibles en la interfaz de usuario del contenedor. El panel de contenido puede contener directamente componentes como JButton, u otros contenedores, como JPanel, que a su vez puede contener componentes. 1 Un contenedor de nivel superior puede tener una barra de menú. Una barra de menú es una tira horizontal que convencionalmente está puesta en la cima de un contenedor y tiene opciones para el usuario. La barra de menú está encima del panel de contenido. Un panel de cristal está encima del panel de contenido. En la siguiente figura se muestra la relación entre un JFrame y su páneles raı́z, de contenido y de cristal. +---------------------+ | JFrame | | +--------------------+ | | Panel raı́z | | | +--------------------+ | | | Barra de menú | | | |--------------------| +---| | Panel de contenido | | | +-------------------+ +---| | Panel de cristal | | | | +---| | | | | | | | +-------------------+ Nota. En el panel de cristal residen los tool tips. Se pueden dibujar sus propios gráficos en este panel “en la cima de” los componentes en un JFrame o JApplet. Si se agrega un MouseListener al panel de cristal, este previene que el ratón dispare eventos en los componentes debajo del panel de cristal en el JFrame. Existe un panel adicional encima del panel raı́z, pero este no es usado frecuentemente por los programadores. Para obtener una referencia al panel de contenido de un JFrame, u otro contenedor de nivel superior, se usa el método getContentPane(). Los métodos add() y remove() usados para agregar y quitar componentes de objetos JFrame y el método setLayoutManager() usado para poner el manejador de diseño tienen esas habilidades porque Java los convierte en versiones más completas. Por ejemplo, las siguientes tres sentencias son equivalentes dentro de una clase que descienda de JFrame: this.getContentPane().add(unBoton); getContentPane().add(unBoton); add(unBoton); En la primera sentencia, this se refiere a la clase JFrame en el cual la sentencia aparece, y getContentPane() da una referencia al panel de contenido. En la segunda sentencia, la referencia this está implicada. En la tercera sentencia, la referencia this y la llamada al método getContentPane() están implicados. 2 No se requiere preocuparse por el panel de contenido si sólo se agregan componentes, se quitan o se pone el manejador de diseño de un JFrame, pero se deberá referir al panel de contenido para todas las otras acciones, tales como poner el color del fondo. Cuando se escribe una aplicación en la cual se tenga que estar usando el panel de contenido, es más eficiente declarar un objeto que represente el panel de contenido que estar llamando al método getContentPane(), como se muestra en el siguiente ejemplo, donde la llamada al método getContentPane() es hecha una vez, su referencia se guarda en una variable, y luego el nombre de referencia es usada con la llamada al método add(): Container con = getContentPane(); con.add(boton1); con.add(boton2); con.add(boton3); En la clase JFrameConContentPane, código 1, se crea un JFrame donde la operación de cierre por defecto no fue puesta y al botón no se le asignó alguna funcionalidad, para que el ejemplo sea sencillo. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import j a v a . awt . ∗ ; import j a v a x . swing . ∗ ; public c l a s s JFrameConContentPane extends JFrame { private f i n a l int TAM = 1 8 0 ; private C o n t a i n e r con = getContentPane ( ) ; private JButton boton = new JButton ( ” Pulsa aquı́” ) ; public JFrameConContentPane ( ) { super ( ” Ventana ” ) ; s e t S i z e (TAM, TAM) ; con . s e t L a y o u t (new FlowLayout ( ) ) ; con . add ( boton ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { JFrameConContentPane marco = new JFrameConContentPane ( ) ; marco . s e t V i s i b l e ( true ) ; } } Código 1: La clase JFrameConContentPane. En el código 1 en la lı́nea 5 la llamada del método getContentPane() se asigna una referencia a un Container llamado con, y luego es usada con los métodos setLayout() y add() en las lı́neas 10 y 11 respectivamente. Cuando se quieren usar otros métodos en vez de add(), remove(), o setLayout() con un JFrame, se debe usar un panel de contenido, como cuando se emplea el método setBackground() usado para cambiar el color del fondo. Si este método se usa con un JFrame el usuario no verá los resultados. 3 2. Clase Color La clase Color define colores para ser usados en aplicaciones, por ejemplo con los métodos setBackground() y setForeground() de la clase Component. Cuando se usa la clase Color, se debe incluir la sentencia import java.awt.Color; al inicio del archivo de la clase. La clase Color define constantes con nombre que representan trece colores, como se muestra en el cuadro 1. Las constantes en Java son escritas usualmente con todas las letras en mayúsculas. Sin embargo, los creadores de Java declararon dos constantes para cada color en la clase Color—una versión con mayúsculas, y la otra con minúsculas. Las primeras versiones de Java contenı́an sólo las constantes con minúsculas. Constante minúsculas BLACK BLUE CYAN DARK GRAY GRAY GREEN LIGHT GRAY MAGENTA ORANGE PINK RED WHITE YELLOW Constante mayúsculas black blue cyan darkgray gray green lightgray magenta orange pink red white yellow Color negro azul cian gris obscuro gris verde gris claro magenta naranja rosa rojo blanco amarillo Cuadro 1: Constantes de la clase Color. Para crear un objeto Color propio se harı́a de la siguiente forma: Color algunColor = new Color(r, g, b); En la sentencia previa, r, g, y b son números que representan las intensidades de rojo, verde, y azul que se quieren en el color. El rango de los números está entre 0 y 255. Para crear el color negro se requiere que los valores de r, g, y b sean cero, y para el color blanco es con 255. En la siguiente sentencia se genera el color púrpura obscuro que tiene componentes para el rojo y el azul, pero carece de verde. Color purpuraObscuro = new Color(100, 0, 100); Se pueden crear más de 16 millones, 224 , de colores personalizados de la forma anterior. Algunos computadoras no pueden mostrar cada uno de los 16 millones de colores posibles; cada computadora muestra el color más cercano que puede al color solicitado. 4 Para determinar la cantidad de los componentes rojo, verde, o azul de algún objeto Color se usan los métodos getRed(), getGreen(), o getBlue() respectivamente. Cada uno de los métodos devuelve un entero. Por ejemplo, para saber la cantidad de rojo en MAGENTA se usa Color.MAGENTA.getRed(). La aplicación JFrameConColor, código 2, pone el color del fondo de un panel de contenido JFrame y los colores del frente y el fondo de un JButton. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import j a v a . awt . ∗ ; import j a v a x . swing . ∗ ; public c l a s s JFrameConColor extends JFrame { private f i n a l int TAM = 1 8 0 ; private C o n t a i n e r con = getContentPane ( ) ; private JButton boton = new JButton ( ” Pulsa aquı́” ) ; public JFrameConColor ( ) { super ( ”Marco” ) ; s e t S i z e (TAM, TAM) ; con . s e t L a y o u t (new FlowLayout ( ) ) ; con . add ( boton ) ; con . setBackground ( C o l o r .YELLOW) ; boton . setBackground ( C o l o r .RED) ; boton . s e t F o r e g r o u n d ( C o l o r .BLUE ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { JFrameConColor marco = new JFrameConColor ( ) ; marco . s e t V i s i b l e ( true ) ; } } Código 2: La aplicación JFrameConColor. 3. Manejadores de diseño Un manejador de diseño es un objeto que controla el tamaño y la posición (que es, el diseño) de componentes dentro de un objeto Container. Dependiendo del manejador de diseño que se asigne a un Container este determina como sus componentes son dimensionados y posicionados. Los manejadores de diseño son clases interfaz que son parte del JDK; estos alinean los componentes para que ni se amontonen, ni se traslapen. Por ejemplo, el manejador de diseño FlowLayout coloca los componentes en renglones de izquierda a derecha a través de su contenedor. Otros manejadores de diseño ponen los componentes en columnas y renglones de tamaño igual o centran los componentes dentro de su contenedor. Cada componente que se pone dentro de un Container puede también ser un Container por sı́ mismo, ası́ que se pueden asignar manejadores de diseño dentro de otros. Java proporciona manejadores de diseño que van desde los muy simples, como FlowLayout y GridLayout, los de propósito especial, como BorderLayout y CardLayout, a los muy flexibles, como GridBagLayout y BoxLayout. En el cuadro 2 se listan cada uno de los manejadores de diseño y las situaciones en las cuales son comúnmente empleados. 5 Manejador BorderLayout FlowLayout GridLayout CardLayout GridBagLayout Descripción Usar para agregar hasta cinco componentes. Usar para agregar componentes de izquierda a derecha; cuando el renglón se llena se pasa automáticamente al siguiente renglón, y cada componente toma su tamaño preferente. Usar para agregar componentes en una malla de renglones y columnas; cada componente es del mismo tamaño. Usar para agregar componentes que sean mostrados uno a la vez. Usar cuando se necesite fijar tamaño, lugar, y restricciones de alineación para cada componente que sea agregado. Cuadro 2: Manejadores de diseño. Clase BorderLayout El manejador BorderLayout es la clase manejadora por defecto para todos los páneles de contenido. Se puede usar la clase BorderLayout con cualquier contenedor que tenga 5 componentes o menos. Cualquiera de los componentes podrı́a ser un contenedor que tenga más componentes. Con el manejador BorderLayout los componentes llenan la pantalla en cinco regiones: norte, sur, este, oeste y centro. Cuando se agrega un componente a un contenedor que usa BorderLayout, el método add() usa dos argumentos: el componente y la región a la cual este es agregado. La clase BorderLayout proporciona cinco constantes con nombre para las regiones—BorderLayout.NORTH, .SOUTH, .EAST, .WEST, y .CENTER—o se pueden usar los String que estas constantes representan: “North”, “South”, “East”, “West”, o “Center”. La aplicación DemoJBorderLayout, código 3, muestra una salida donde han sido puestos cinco botones. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import j a v a . awt . ∗ ; import j a v a x . swing . ∗ ; public c l a s s DemoJBorderLayout extends JFrame { private JButton bn = new JButton ( ” Botón Norte ” ) ; private JButton bs = new JButton ( ” Botón Sur ” ) ; private JButton be = new JButton ( ” Botón Este ” ) ; private JButton bo = new JButton ( ” Botón Oeste ” ) ; private JButton bc = new JButton ( ” Botón Centro ” ) ; private C o n t a i n e r con = getContentPane ( ) ; public DemoJBorderLayout ( ) { con . s e t L a y o u t (new BorderLayout ( ) ) ; con . add ( bn , BorderLayout .NORTH) ; con . add ( bs , BorderLayout .SOUTH) ; con . add ( be , BorderLayout .EAST ) ; con . add ( bo , BorderLayout .WEST) ; con . add ( bc , ” Center ” ) ; s e t S i z e (400 , 150); } public s t a t i c void main ( S t r i n g [ ] a r g s ) { DemoJBorderLayout ventana = 6 21 22 23 24 new DemoJBorderLayout ( ) ; ventana . s e t V i s i b l e ( true ) ; } } Código 3: Aplicación DemoJBorderLayout. Nota. Cuando se usa BorderLayout, se pueden usar las constantes PAGE START, PAGE END, LINE START, LINE END, y CENTER en vez NORTH, SOUTH, EAST, WEST, y CENTER. Las constantes dadas corresponden a posiciones que se podrı́an tener en una hoja impresa. Si se agrega la siguiente sentencia import, se puede referir a CENTER en vez de BorderLayout.CENTER: import static java.awt.BorderLayout.*; Cuando se colocan cinco componentes en un contenedor y se usa BorderLayout, cada componente llena una región entera, como se observa al ejecutar la aplicación DemoJBorderLayout. Cuando la aplicación se ejecuta, Java determina el tamaño exacto de cada componente basado en el contenido del componente. Cuando se redimensiona un Container que use BorderLayout, las regiones también cambian en tamaño. Si el contenedor se hace más ancho las regiones norte, sur, y centro se hacen más anchas, pero las otras restantes no cambian. Si se incrementa la altura, las regiones este, oeste, y centro se hacen más altas, pero las otras permanecen sin cambio. Cuando se usa BorderLayout, no se requiere agregar componentes en cada una de las cinco regiones. Si se agregan menos componentes, cualquier región vacı́a desaparece, y las restantes se expanden para llenar el espacio disponible. Si cualquiera de las regiones norte, sur, este, u oeste fueron dejadas vacı́as, el área central se expande en las áreas vacı́as. Sin embargo, si el área central es dejada, ni la área norte, sur, este, u oeste cambia. Un error común cuando se usa BorderLayout es agregar un componente a un panel de contenido o marco sin nombrar una región. Esto puede resultar que algunos de los componentes no sea visible. Clase FlowLayout Se puede usar el manejador FlowLayout para arreglar los componentes en renglones a lo ancho de un Container. Con un FlowLayout, cada componente que sea agregado es colocado a la derecha de los componentes previamente agregados en un renglón; o, si el renglón actual está lleno, el componente es puesto al inicio de un nuevo renglón. Cuando se usa BorderLayout, los componentes que se agregan llenan sus regiones. Sin embargo, cuando se usa FlowLayout, cada Component retiene su tamaño por defecto o tamaño preferente. Por ejemplo, el tamaño preferente de un JButton es el tamaño que es lo suficientemente grande para tener el texto del JButton. Cuando se usa un BorderLayout y luego se redimensiona la ventana, los componentes cambian su tamaño porque sus regiones cambian. Cuando se usa FlowLayout y luego se redimensiona la ventana, cada componente retiene su tamaño, pero este podrı́a obscurecerse parcialmente o cambiar de posición. La clase FlowLayout contiene tres constantes que se pueden usar para alinear Componentes en un Container: FlowLayout.LEFT 7 FlowLayout.CENTER FlowLayout.RIGHT Si no se indica la alineación, los componentes son alineados al centro en un Container con FlowLayout por defecto. En la aplicación DemoJFlowLayout, código 4, un objeto FlowLayout llamado manejador es usado para poner el diseño del panel de contenido. Cuando el usuario pulsa un botón, en las lı́neas 19—22 el método actionPerformed() cambia la alineación a la izquierda o a la derecha usando el método setAlignment(). Al ejecutar la DemoJFlowLayout se debe observar como los componentes JButton son reposicionados después de que se pulsa en el botón izquierdo, y después de hacerlo en el botón derecho. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import j a v a . awt . ∗ ; import j a v a . awt . e v e n t . ∗ ; import j a v a x . swing . ∗ ; public c l a s s DemoJFlowLayout extends JFrame implements A c t i o n L i s t e n e r { private JButton b i = new JButton ( ” Botón I z q u i e r d o ” ) ; private JButton bd = new JButton ( ” Botón Derecho ” ) ; private C o n t a i n e r con = getContentPane ( ) ; private FlowLayout manejador = new FlowLayout ( ) ; public DemoJFlowLayout ( ) { con . s e t L a y o u t ( manejador ) ; con . add ( b i ) ; con . add ( bd ) ; bi . addActionListener ( this ) ; bd . a d d A c t i o n L i s t e n e r ( t h i s ) ; s e t S i z e (500 , 100); } public void a c t i o n P e r f o r m e d ( ActionEvent e v e n t ) { Object s o u r c e = e v e n t . g e t S o u r c e ( ) ; i f ( s o u r c e == b i ) manejador . s e t A l i g n m e n t ( FlowLayout . LEFT ) ; else manejador . s e t A l i g n m e n t ( FlowLayout . RIGHT ) ; con . i n v a l i d a t e ( ) ; con . v a l i d a t e ( ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { DemoJFlowLayout ventana = new DemoJFlowLayout ( ) ; ventana . s e t V i s i b l e ( true ) ; } } Código 4: Aplicación DemoJFlowLayout. Nota. Las sentencias de las lı́neas 23 y 24 en el método actionPerformed() del código 4 llaman a los métodos invalidate() y validate(), respectivamente. La llamada invalidate() marca el contenedor, y cualquiera de sus padres, como necesitado de ser arreglado. La llamada validate() causa que los componentes sean redispuestos de acuerdo al manejador nuevo asignado. 8 Clase GridLayout Si quiere acomodar componentes en renglones y columnas del mismo tamaño, se puede usar la clase manejador GridLayout. Cuando se crea un objeto GridLayout se indica la cantidad de renglones y columnas deseadas, y entonces la superficie es dividida en una cuadrı́cula. Con la siguiente sentencia se crea un GridLayout anónimo con cuatro renglones y cinco columnas en un Container llamado con: con.setLayout(new GridLayout(4, 5)); Cuando se agregan componentes a un GridLayout, estos son posicionados en secuencia de izquierda a derecha a través de cada renglón. No se puede saltar una posición o indicar una posición exacta para un componente. Sin embargo, se podrı́a agregar una etiqueta blanca para simular el salto de una posición. Se puede también indicar una separación vertical y horizontal medida en pı́xeles, con dos argumentos adicionales. La aplicación DemoJGridLayout, código 5, en la lı́nea 9 establece un GridLayout con tres renglones y dos columnas, e intervalos horizontal y vertical de cinco pı́xeles cada uno. Cinco componentes JButton son agregadas al panel de contenido recuperado del JFrame. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import j a v a . awt . ∗ ; import j a v a x . swing . ∗ ; public c l a s s DemoJGridLayout extends JFrame { private JButton b1 = new JButton ( ” Botón 1 ” ) ; private JButton b2 = new JButton ( ” Botón 2 ” ) ; private JButton b3 = new JButton ( ” Botón 3 ” ) ; private JButton b4 = new JButton ( ” Botón 4 ” ) ; private JButton b5 = new JButton ( ” Botón 5 ” ) ; private GridLayout d i s e n o = new GridLayout ( 3 , 2 , 5 , 2 ) ; private C o n t a i n e r con = getContentPane ( ) ; public DemoJGridLayout ( ) { con . s e t L a y o u t ( d i s e n o ) ; con . add ( b1 ) ; con . add ( b2 ) ; con . add ( b3 ) ; con . add ( b4 ) ; con . add ( b5 ) ; s e t S i z e (200 , 200); } public s t a t i c void main ( S t r i n g [ ] a r g s ) { DemoJGridLayout ventana = new DemoJGridLayout ( ) ; ventana . s e t V i s i b l e ( true ) ; } } Código 5: Aplicación DemoJGridLayout. Cuando se ejecuta la aplicación DemoJGridLayout, los componentes son puestos en el panel a través de los tres renglones. Como hay seis posiciones pero sólo cinco componentes, un lugar está sin usar. Con GridLayout, se puede indicar la cantidad de renglones y usar cero para la cantidad de columnas para permitir que el manejador de diseño determine la cantidad de columnas, de igual forma se 9 puede indicar la cantidad de columnas y usar cero para la cantidad de renglones, para que el manejador calcula la cantidad de renglones. Cuando se intenta decidir entre usar un GridLayout o un FlowLayout, considerar lo siguiente: Usar GridLayout cuando se quiere a los componentes en renglones y columnas fijas, y se quiere que el tamaño de los componentes llene el espacio disponible. Usar FlowLayout si se quiere que Java determine los renglones y las columnas, en vez de algo fijo, para que los componentes retengan su tamaño “natural” para que sus contenidos sean completamente visibles. Clase CardLayout El manejador CardLayout genera una pila de contenedores o componentes. Cada componente en el grupo es referido como una carta, y cada carta puede ser cualquier tipo de componente, como un JButton, un JLabel, o un JPanel. Se emplea un CardLayout cuando se quiere que componentes múltiples compartan el mismo espacio de presentación. Un manejador de cartas es creado de la clase CardLayout usando uno de dos constructores: CardLayout() crea un diseño de cartas sin un intervalo horizontal o vertical. CardLayout(int hInt, int vInt) crea un diseño de cartas con los intervalos horizontales y verticales especificados. Los intervalos horizontales son colocados en las aristas izquierda y derecha. Los intervalos verticales son colocados en las aristas de la cima y el fondo. La aplicación DemoJCardLayout, código 6, usa un manejador CardLayout para crear una pila de JButton que contienen las etiquetas “As de Corazones”, “Tres de Espadas”, “Reina de Tréboles”. En el constructor de la clase, se ocupa una versión ligeramente diferente del método add() para agregar componentes a un panel de contenido cuyo manejador de diseño es CardLayout. El formato es: add(unString, unContenedor); En esta sentencia, unString representa el nombre que se quiere usar para identificar la carta componente que es agregada. 1 2 3 4 5 6 7 8 9 10 import j a v a . awt . ∗ ; import j a v a . awt . e v e n t . ∗ ; import j a v a x . swing . ∗ ; public c l a s s DemoJCardLayout extends JFrame implements A c t i o n L i s t e n e r { private CardLayout c a r t a s = new CardLayout ( ) ; private JButton b1 = new JButton ( ”As de Corazones ” ) ; private JButton b2 = new JButton ( ” Tres de Espadas ” ) ; private JButton b3 = new JButton ( ” Reina de Tré b o l e s ” ) ; private C o n t a i n e r con = getContentPane ( ) ; public DemoJCardLayout ( ) { 10 con . s e t L a y o u t ( c a r t a s ) ; con . add ( ” a s ” , b1 ) ; b1 . a d d A c t i o n L i s t e n e r ( t h i s ) ; con . add ( ” t r e s ” , b2 ) ; b2 . a d d A c t i o n L i s t e n e r ( t h i s ) ; con . add ( ” r e i n a ” , b3 ) ; b3 . a d d A c t i o n L i s t e n e r ( t h i s ) ; s e t S i z e (200 , 100); 11 12 13 14 15 16 17 18 19 20 } public void a c t i o n P e r f o r m e d ( ActionEvent e ) { c a r t a s . next ( getContentPane ( ) ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { DemoJCardLayout ventana = new DemoJCardLayout ( ) ; ventana . s e t V i s i b l e ( true ) ; } 21 22 23 24 25 26 27 } Código 6: Aplicación DemoJCardLayout. En un programa que tenga un manejador CardLayout, un cambio de carta es disparado por la acción del usuario. En el programa DemoJCardLayout, cada JButton dispara el método actionPerformed(). Dentro de este método, la sentencia next(getContentPane()) cambia a la siguiente carta del contenedor. El orden de las cartas depende del orden en cual fueron agregadas al contenedor. Se puede también usar previous(getContentPane());, first(getContentPane());, y last(getContentPane()); para cambiar a la carta previa, primera, y última, respectivamente. Se puede ir a una carta especı́fica usando el nombre String asignado en la llamada al método add(). Para la aplicación del código 6, la siguiente sentencia deberá mostrar “Tres de Espadas” porque “tres” es usado como el primer argumento cuando el objeto b2 es agregado al panel de contenido en el constructor DemoJCardLayout: cards.show(getContentPane(), "tres"); Al ejecutar la aplicación DemoJCardLayout se muestra la primera carta, “As de Corazones”, y luego que el usuario pulsa una vez, aparece “Tres de Espadas”, y en la segunda pulsación “Reina de Tréboles”. Como cada JButton es una carta, cada JButton usa el área de visualización completamente en el contenedor. Si el usuario continúa pulsando los botones carta, las cartas se ciclan en orden. Manejadores de diseño avanzado El manejador GridBagLayout permite agregar componentes en lugares especı́ficos en una cuadrı́cula, al igual que se puede indicar que los componentes se expandan en múltiples renglones y columnas. Por ejemplo, si se quiere crear un JPanel con seis JButton, en el cual dos de los JButton sean el doble de anchos que los otros, se puede usar GridBagLayout. Esta clase es difı́cil de usar porque se debe poner la posición y el tamaño para cada componente, y más de veinte métodos están asociados con la clase. 11 Otra opción de manejador de diseño es el manejador BoxLayout, el cual permite que varios componentes estén puestos verticalmente u horizontalmente. Los componentes no se reacomodan cuando el marco es redimensionado, por ejemplo un arreglo vertical de componentes permanece vertical a pesar del redimensionado. Actividad 1. Crear un JFrame que use los diferentes manejadores de diseño vistos en esta sección. 4. JPanel Los manejadores BorderLayout, FlowLayout, GridLayout, y Cardlayout proporcionan una cantidad limitada de arreglos de pantalla si solo se pudiera colocar un solo Component en una sección del diseño. Pero, se puede incrementar la cantidad de arreglos de componentes posibles usando la clase JPanel. Un JPanel es un plano, superficie sin borde que puede tener componentes UI de peso ligero. Enseguida se muestra la jerarquı́a de herencia de la clase JPanel. java.lang.Object | +--java.awt.Component | +---java.awt.Container | +---javax.swing.JComponent | +---javax.swing.JPanel De la figura previa, se ve que cada JPanel es un Container; ası́ que se puede usar un JPanel para tener otros componentes UI, tales como JButton, JCheckBox, o incluso otros JPanel. Usando un JPanel dentro de otro, se puede crear una variedad infinita de diseños de pantalla. El manejador de diseño por defecto para cada JPanel es FlowLayout. Para agregar un componente a un JPanel, se llama al método add() del contenedor, usando el componente como el argumento. La aplicación JFrameConJPanels, código 7, extiende la clase JFrame. Un JButton es agregado a un JPanel llamado panel1, y dos JButton más son agregados a otro JPanel llamado panel2. Luego panel1 y panel2 son agregados a panel de contenido de un JFrame. 1 2 3 4 5 6 7 8 9 10 11 12 import j a v a . awt . ∗ ; import j a v a x . swing . ∗ ; import s t a t i c j a v a . awt . C o l o r . ∗ ; public c l a s s JFrameConJPanels extends JFrame { private f i n a l int ANCHO = 2 5 0 ; private f i n a l int ALTO = 1 2 0 ; private JButton boton1 = new JButton ( ”Uno” ) ; private JButton boton2 = new JButton ( ”Dos” ) ; private JButton boton3 = new JButton ( ” Tres ” ) ; public JFrameConJPanels ( ) { super ( ”JFrame con J P a n e l s ” ) ; s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE ) ; 12 JPanel p a n e l 1 = new JPanel ( ) ; JPanel p a n e l 2 = new JPanel ( ) ; C o n t a i n e r con = getContentPane ( ) ; con . s e t L a y o u t (new FlowLayout ( ) ) ; con . add ( p a n e l 1 ) ; con . add ( p a n e l 2 ) ; p a n e l 1 . add ( boton1 ) ; p a n e l 1 . setBackground (BLUE ) ; p a n e l 2 . add ( boton2 ) ; p a n e l 2 . add ( boton3 ) ; p a n e l 2 . setBackground (RED) ; s e t S i z e (ANCHO, ALTO) ; 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 } public s t a t i c void main ( S t r i n g [ ] a r g s ) { JFrameConJPanels ventana = new JFrameConJPanels ( ) ; ventana . s e t V i s i b l e ( true ) ; } } Código 7: Aplicación JFrameConJPanels. Al ejecutar la aplicación JFrameConJPanels se deben mostrar los dos JPanel que fueron agregados al JFrame, el primero con un botón y color azul de fondo, y el otro con dos botones y color rojo de fondo. Cuando se crea un objeto JPanel, se puede usar uno de cuatro constructores disponibles. Los diferentes constructores permiten usar valores por defecto o indicar un manejador de diseño y si el JPanel es doble búfer. Si se indica doble búfer, la cual es la estrategia de búfer por defecto, se indica que el espacio de memoria adicional será usado para dibujar el JPanel fuera de pantalla cuando sea actualizado. Con doble búfer, un JPanel redibujado es mostrado sólo cuando este está completo; esto proporciona al usuario con pantallas actualizadas que no parpadeen mientras son redibujadas. Los cuatro constructores son los siguientes: JPanel() crea un JPanel con doble búfer y un diseño de flujo. JPanel(boolean esDobleBúfer) crea un JPanel con un diseño de flujo y la estrategia de doble búfer indicada. JPanel(LayoutManager dise~ no) crea un JPanel con el manejador de diseño indicado y doble búfer. JPanel(LayoutManager dise~ no, boolean esDobleBúfer) crea un JPanel con el manejador de diseño y la estrategia de búfer indicados. Nota. Cuando se emplea doble búfer, el área de pantalla visible es llamada el área primaria, y la imagen fuera de pantalla es llamada el búfer de respaldo. El acto de copiar los contenidos de una área a otra es frecuentemente referida como una transferencia lineal de bloques, o blitting. El doble búfer previene la “despedazadera”, el efecto visual que ocurre cuando se ven partes de imágenes diferentes porque el redibujado no es lo suficientemente rápido. Como la mayorı́a de las prestaciones, doble búfer tiene un costo de requerimientos adicionales de memoria. 13 Hay varias formas para crear un JPanel que use un manejador BorderLayout: Se crea un diseño con nombre y se usa este como un argumento en un constructor JPanel: BorderLayout borde = new BorderLayout(); JPanel miPanel = new JPanel(borde); Se usa un manejador de diseño anónimo con el constructor JPanel: JPanel miPanel = new JPanel(new BorderLayout()); Se crea un JPanel y luego se pone su manejador de diseño usando el método setLayout(): JPanel miPanel = new JPanel(); miPanel.setLayout(new BorderLayout()); Cuando un JPanel tendrá un manejador diferente de FlowLayout, es preferible especificar el manejador de diseño cuando se crea el JPanel por cuestiones de rendimiento. Si se crea el JPanel primero y luego se cambia su manejador, se crea un objeto FlowLayout innecesario para la instanciación original. Se agregan componentes a un JPanel con el método add(). La aplicación JDemoMuchosPaneles, código 8, es un JFrame que contiene cuatro JPanel y doce JButton donde cada uno muestra un número con letras. Al panel de contenido de un JFrame automáticamente se le asigna un BorderLayout, y a cada JPanel se le asigna un GridLayout o un FlowLayout y son colocados en una de las regiones, dejando la región norte vacı́a. Uno o más JButton son luego colocados en cada JPanel. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import j a v a . awt . ∗ ; import j a v a x . swing . ∗ ; public c l a s s JDemoMuchosPaneles extends JFrame { // Doce b o t o n e s private JButton boton01 = new JButton ( ”Uno” ) ; private JButton boton02 = new JButton ( ”Dos” ) ; private JButton boton03 = new JButton ( ” Tres ” ) ; private JButton boton04 = new JButton ( ” Cuatro ” ) ; private JButton boton05 = new JButton ( ” Cinco ” ) ; private JButton boton06 = new JButton ( ” S e i s ” ) ; private JButton boton07 = new JButton ( ” S i e t e ” ) ; private JButton boton08 = new JButton ( ”Ocho” ) ; private JButton boton09 = new JButton ( ”Nueve” ) ; private JButton boton10 = new JButton ( ” Diez ” ) ; private JButton boton11 = new JButton ( ”Once” ) ; private JButton boton12 = new JButton ( ” Doce ” ) ; // Cuatro p a n e l e s private JPanel p a n e l 0 1 = new JPanel (new GridLayout ( 2 , 0 ) ) ; private JPanel p a n e l 0 2 = new JPanel (new FlowLayout ( ) ) ; private JPanel p a n e l 0 3 = new JPanel (new FlowLayout ( ) ) ; private JPanel p a n e l 0 4 = new JPanel (new GridLayout ( 2 , 0 ) ) ; public JDemoMuchosPaneles ( ) { s e t L a y o u t (new BorderLayout ( ) ) ; 14 add ( panel01 , BorderLayout .WEST) ; add ( panel02 , BorderLayout .CENTER) ; add ( panel03 , BorderLayout .SOUTH) ; add ( panel04 , BorderLayout .EAST ) ; p a n e l 0 1 . add ( boton01 ) ; p a n e l 0 1 . add ( boton02 ) ; p a n e l 0 1 . add ( boton03 ) ; p a n e l 0 2 . add ( boton04 ) ; p a n e l 0 2 . add ( boton05 ) ; p a n e l 0 2 . add ( boton06 ) ; p a n e l 0 3 . add ( boton07 ) ; p a n e l 0 4 . add ( boton08 ) ; p a n e l 0 4 . add ( boton09 ) ; p a n e l 0 4 . add ( boton10 ) ; p a n e l 0 4 . add ( boton11 ) ; p a n e l 0 4 . add ( boton12 ) ; s e t S i z e (500 , 250); 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 } public s t a t i c void main ( S t r i n g [ ] a r g s ) { JDemoMuchosPaneles ventana = new JDemoMuchosPaneles ( ) ; ventana . s e t V i s i b l e ( true ) ; } } Código 8: La aplicación JDemoMuchosPaneles. Cuando se ejecuta la aplicación JDemoMuchosPaneles el usuario puede hacer la ventana horizontalmente más grande para observar como algunos botones aparecen y se reacomodan, en particular los que están en el JPanel que ocupa la región central. Nota. Si se crea un programa con muchos botones y páneles como los del código 8, se deberı́a preferir crear arreglos de los componentes en vez de manejar muchos nombres individualmente. Nota. Los contenedores Swing, excepto JPanel, y los páneles de contenido proporcionan métodos interfaz de programación de aplicaciones (API) que se deberı́an usar en vez del método add(). GridLayout proporciona renglones y columnas que son similares a los arreglos bidimensionales. Por lo tanto, este se usa para mostrar arreglos de objetos. La aplicación Tablero, código 9, muestra un patrón de ocho renglones y ocho columnas en colores alternantes. El JPanel colocado en el panel de contenido tiene un GridLayout de ocho por ocho. 64 JPanel son declarados, y en un ciclo, uno por uno, son instanciados y asignados a una sección de la cuadrı́cula, lı́neas 19—21. Después de que cada conjunto de ocho JPanel es asignado a la cuadrı́cula, cuando x es divisible por 8, el color1 y el color2 son intercambiados, para que el primer renglón inicie con un cuadro azul, el segundo inicie con un cuadro blanco, etc. Dentro de cada renglón, todos los cuadros con posición par son llenados con un color, y los otros cuadros con otro color. Ejecutar la aplicación Tablero para ver la salida. 1 2 3 import j a v a . awt . ∗ ; import j a v a x . swing . ∗ ; public c l a s s T a b l e r o extends JFrame { 15 private private private private private private f i n a l int RENS = 8 ; f i n a l int COLS = 8 ; f i n a l int SEP = 2 ; f i n a l int NUM = RENS ∗ COLS ; int x ; JPanel pane = new JPanel ( new GridLayout (RENS, COLS, SEP , SEP ) ) ; private JPanel [ ] p a n e l = new JPanel [NUM] ; private C o l o r c o l o r 1 = C o l o r .WHITE; private C o l o r c o l o r 2 = C o l o r .BLUE; private C o l o r tempColor ; public T a b l e r o ( ) { super ( ” T a b l e r o ” ) ; s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE ) ; add ( pane ) ; f o r ( x = 0 ; x < NUM; ++x ) { p a n e l [ x ] = new JPanel ( ) ; pane . add ( p a n e l [ x ] ) ; i f ( x % COLS == 0 ) { tempColor = c o l o r 1 ; color1 = color2 ; c o l o r 2 = tempColor ; } i f ( x % 2 == 0 ) p a n e l [ x ] . setBackground ( c o l o r 1 ) ; else p a n e l [ x ] . setBackground ( c o l o r 2 ) ; } } public s t a t i c void main ( S t r i n g [ ] a r g s ) { T a b l e r o ventana = new T a b l e r o ( ) ; f i n a l int TAM = 3 0 0 ; ventana . s e t S i z e (TAM, TAM) ; ventana . s e t V i s i b l e ( true ) ; } 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 } Código 9: Aplicación Tablero. Cuando se crea la clase Tablero, se podrı́a estar tentado a crear sólo dos JPanel, uno azul y uno blanco, y agregarlo al panel de contenido varias veces. Sin embargo, cada componente GUI puede ser contenido una sola vez. Si un componente ya está en un contenedor y se intenta agregar otro contenedor, el componente será quitado del primer contenedo y luego agregado al segundo. 5. JScrollPane Cuando componentes en una UI Swing requieren más área de visualización que la les ha sido asignada, se puede usar un contenedor JScrollPane para tener los componentes en una forma que permitan al usuario desplazar partes no visibles inicialmente del panel en la vista. Un JScrollPane tiene barras de desplazamiento a un lado o en el fondo, o en ambos, con una área de visualización 16 llamada una ventana (viewport). La siguiente figura muestra la jerarquı́a de herencia de la clase JScrollPane. java.lang.Object | +--java.awt.Component | +---java.awt.Container | +---javax.swing.JComponent | +---javax.swing.JScrollPanel Hay cuatro constructores JScrollPane que son: JScrollPane() crea un JScrollPane vacı́o en el cual las barras de desplazamiento horizontal y vertical aparecen cuando se ocupan. JScrollPane(Component) crea un JScrollPane que muestra el contenido del componente indicado. JScrollPane(Component, int, int) crea un JScrollPane que muestra el componente especificado e incluye especificaciones de las barras de desplazamiento horizontal y vertical. JScrollPane(int, int) crea un JScrollPane con ambas especificaciones de las barras de desplazamiento horizontal y vertical. Cuando se crea un panel de desplazamiento simple usando el constructor sin argumentos, como en el siguiente ejemplo, las barras de desplazamiento horizontal y vertical aparecen solo si se ocupan, es decir, si el contenido del panel no puede ser mostrado completamente: JScrollPane unScrollPane = new JScrollPane(); Para forzar la aparición de una barra de desplazamiento, se pueden usar las constantes definidas en la interfaz JScrollPaneConstants: ScrollPaneConstants.HORIZONTAL SCROLLBAR AS NEEDED ScrollPaneConstants.HORIZONTAL SCROLLBAR ALWAYS ScrollPaneConstants.HORIZONTAL SCROLLBAR NEVER ScrollPaneConstants.VERTICAL SCROLLBAR AS NEEDED ScrollPaneConstants.VERTICAL SCROLLBAR ALWAYS ScrollPaneConstants.VERTICAL SCROLLBAR NEVER El siguiente código crea un panel de desplazamiento que muestra una imagen llamada dibujo, una barra de desplamiento vertical, y sin barra de desplazamiento horizontal: 17 JScrollPane desplaza = new JScrollPane(dibujo, ScrollPaneConstants.VERTICAL SCROLLBAR ALWAYS, ScrollPaneConstants.HORIZONTAL SCROLLBAR NEVER); La aplicación DemoJScroll, código 10, tiene una etiqueta con una fuente grande que se agregó a un panel. El panel de desplazamiento llamado desplaza incluye el panel y dos barras de desplazamiento. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import j a v a . awt . ∗ ; import j a v a x . swing . ∗ ; public c l a s s DemoJScroll extends JFrame { private JPanel p a n e l = new JPanel ( ) ; private J S c r o l l P a n e d e s p l a z a = new J S c r o l l P a n e ( panel , S c r o l l P a n e C o n s t a n t s .VERTICAL SCROLLBAR ALWAYS, S c r o l l P a n e C o n s t a n t s .HORIZONTAL SCROLLBAR ALWAYS ) ; private JLabel l a b e l = new JLabel ( ” Hace o c h e n t a y s i e t e ” ) ; private Font fontGrande = new Font ( ” A r i a l ” , Font . PLAIN , 3 0 ) ; private C o n t a i n e r con ; public DemoJScroll ( ) { super ( ” DemoJScroll ” ) ; s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE ) ; con = getContentPane ( ) ; l a b e l . s e t F o n t ( fontGrande ) ; con . add ( d e s p l a z a ) ; p a n e l . add ( l a b e l ) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { f i n a l int ANCHO = 1 8 0 ; f i n a l int ALTO = 1 0 0 ; DemoJScroll unaVentana = new DemoJScroll ( ) ; unaVentana . s e t S i z e (ANCHO, ALTO) ; unaVentana . s e t V i s i b l e ( true ) ; } } Código 10: Aplicación DemoJScroll. El objeto DemoJScroll en el programa del código 10 es puesto a propósito con un tamaño suficientemente pequeño (180x100) ası́ una sola parte de la etiqueta que contiene es visible a la vez. Un usuario puede deslizar las barras de desplazamiento para ver la etiqueta completa. Al ejecutar la aplicación DemoJScroll muestra la ventana con barras de desplazamiento en dos lugares. 6. Eventos y su manejo La clase padre de todos los eventos es EventObject, la cual desciende de la clase Object. EventObject es el padre de AWTEvent, la cual a su vez es el padre de clases de eventos especı́ficos como ActionEvent e ItemEvent. La clase abstracta AWTEvent está contenida en el paquete java.awt.event. La siguiente figura ilustra la jerarquı́a de herencia de estas relaciones. 18 java.lang.Object | +--java.util.EventObject | +---java.awt.AWTEvent | +---java.awt.event.ActionEvent +---java.awt.event.AdjustmentEvent +---java.awt.event.ItemEvent +---java.awt.event.TextEvent +---java.awt.event.ComponentEvent | +---java.awt.event.ContainerEvent +---java.awt.event.FocusEvent +---java.awt.event.PaintEvent +---java.awt.event.WindowEvent +---java.awt.event.InputEvent | +---java.awt.event.KeyEvent +---java.awt.event.MouseEvent En la figura previa, ComponentEvent es padre de varias clases de evento, incluyendo a InputEvent, la cual es padre de KeyEvent y MouseEvent. El árbol para eventos tiene descendencia que va lejos, pero los nombres de las clases son simples, y comparten tareas básicas dentro de los programas. Por ejemplo, ActionEvent son generados por componentes donde los usuarios pueden pulsar, como JButton y JCheckBox; TextEvent son generados por componentes en los cuales el usuario ingresa texto, tales como JTextField. MouseEvent incluyen determinar la posición del apuntador del ratón y la distinción entre una pulsación simple y una doble. El cuadro 3 lista algunas acciones comunes del usuario y los eventos que son generados por ellos. Acción del usuario Pulsar un botón Pulsar un componente Pulsar un elemento en un cuadro de lista Pulsar un elemento en una casilla de verificación Cambiar texto en un campo de texto Abrir una ventana Iconificar una ventana Presionar una tecla Tipo de evento resultante ActionEvent MouseEvent ItemEvent ItemEvent TextEvent WindowEvent WindowEvent KeyEvent Cuadro 3: Ejemplos de acciones del usuario y sus tipos de eventos resultantes. Como ActionEvent involucra al ratón, es fácil confundirlo con MouseEvent. Si se está interesado en ActionEvent, la atención está en los cambios de un componente, por ejemplo, un JButton en un JFrame siendo presionado; si se está interesado en MouseEvent, el foco está en lo que el usuario hace manualmente con el ratón, por ejemplo, pulsar el botón izquierdo del ratón. 19 Cuando se escriben programas con GUI, siempre se están manejando eventos que se originan con el ratón o teclas en componentes o contenedores especı́ficos. El sistema operativo notifica al usuario cuando un AWTEvent ocurre, por ejemplo, cuando el ratón es pulsado. Se pueden ignorar los AWTEvent, y por lo tanto no producirán algún efecto. Para tener cuidado de los eventos se puede implementar una interfaz apropiada para la clase. Cada clase evento mostrada en el cuadro 3 tiene una interfaz receptora asociada con este, ası́ para cada clase evento, <nombre>Evento, hay una interfaz similar llamada <nombre>Listener. Por ejemplo, ActionEvent tiene una interfaz ActionListener. La clase MouseEvent tiene un receptor adicional además de MouseListener que es MouseMotionListener. Cada método de la interfaz <nombre>Listener tiene el tipo de regreso void, y cada uno toma un argumento: un objeto que es una instancia de la correspondiente clase <nombre>Evento. Ası́, la interfaz ActionListener tiene un método manejador de evento llamado actionPerformed(), y su cabecera es void actionPerformed(ActionEvent e). Cuando una acción sucede, el método actionPerformed() se ejecuta, y e representa una instancia de aquel evento. En vez de implementar una clase receptora, se puede extender una clase adaptadora. Una clase adaptadora implementa todos los métodos en una interfaz, con un cuerpo vacı́o para cada método. Por ejemplo, la clase MouseAdapter da un método vacı́o para todos los métodos contenidos en MouseListener. La ventaja de extender una clase adaptadora en vez de implementar una interfaz receptora es que sólo se necesita escribir los métodos que se quieren usar. Si un receptor tiene un sólo método, no hay necesidad de un adaptador, como con ActionListener. Si se usa un receptor o un adaptador, se crea un manejador de evento cuando se escribe código para los métodos receptores; es decir, se le dice a la clase como manejar el evento. Después de crear el manejador, se debe registrar una instancia de la clase con el componente que se quiere que el evento afecte. Para cualquier <name>Listener, se debe usar la forma object.add<nombre>Listener(Component) para registrar un objeto con el Component que escuchará objetos. Los métodos add<nombre>Listener(), tales como addActionListener() y addItemListener(), todos trabajan de la misma forma. Ellos registran un receptor con un Component, regresan void, y toman un objeto <nombre>Listener como un argumento. Por ejemplo, si un JFrame es un ActionListener y contiene un JButton llamado presióname, entonces la siguiente sentencia registra este JFrame como un receptor para el JButton presióname: presióname.addActionListener(this); El cuadro 4 lista los eventos con sus receptores y manejadores. Ejemplo de manejo-evento KeyListener Se usa la interfaz KeyListener cuando se está interesado en acciones que el usuario inicia desde el teclado. La interfaz KeyListener contiene tres métodos: keyPressed(), keyTyped(), y keyReleased(). Para la mayorı́a de las aplicaciones de teclado, no es importante si se realiza la acción cuando el usuario primero presiona una tecla, durante la presión de la tecla, o hasta que la tecla sea soltada; probablemente estos eventos ocurren en secuencia rápida. Sin embargo, en aquellas ocasiones que no se quiere tomar acción mientras el usuario mantiene presionada la 20 Evento ActionEvent ItemEvent TextEvent AdjustmentEvent Receptor(es) ActionListener ItemListener TextListener AdjustmentListener ContainerEvent ContainerListener ComponentEvent ComponentListener FocusEvent FocusListener MouseEvent MouseListener MouseMotionListener KeyListener KeyEvent WindowEvent WindowListener MouseWheelEvent MouseWheelListener Manejador(es) actionPerformed(ActionEvent) itemStateChanged(ItemEvent) textValueChanged(TextEvent) adjustmentValueChanged(AdjustmentEvent) componentAdded(ContainerEvent) componentRemoved(ContainerEvent) componentMoved(ComponentEvent) componentHidden(ComponentEvent) componentResized(ComponentEvent) componentShown(ComponentEvent) focusGained(FocusEvent) focusLost(FocusEvent) mousePressed(MouseEvent) mouseReleased(MouseEvent) mouseEntered(MouseEvent) mouseExited(MouseEvent) mouseClicked(MouseEvent) mouseDragged(MouseEvent) mouseMoved(MouseEvent) keyPressed(KeyEvent) keyTyped(KeyEvent) keyReleased(KeyEvent) windowActivated(WindowEvent) windowClosing(WindowEvent) windowClosed(WindowEvent) windowDeiconified(WindowEvent) windowIconified(WindowEvent) windowOpened(WindowEvent) mouseWheelMoved(MouseWheelEvent) Cuadro 4: Ejemplos de acciones del usuario y sus tipos de eventos resultantes. 21 tecla, se pueden colocar las acciones en el método keyReleased(). Lo mejor es usar el método keyTyped() cuando se quiere descubrir que carácter fue tecleado. Cuando el usuario presiona una tecla que no genera un carácter, tal como una tecla función, llamadas a veces una tecla acción, keyTyped() no es ejecutada. Los métodos keyPressed() y keyReleased() dan las únicas formas para obtener información acerca de las teclas que no generan caracteres. La clase KeyEvent tiene constantes conocidas como códigos de teclas virtuales que representan las teclas que han sido presionadas. Por ejemplo, cuando se teclea A, dos códigos de teclas virtuales son generados: Cambio y “a”. Las constantes de códigos de teclas virtuales tienen nombres como VK SHIFT y VK ALT. La aplicación DemoJKeyFrame, código 11 usa el método keyTyped() para saber cual fue la última tecla presionada por el usuario. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import j a v a . awt . ∗ ; import j a v a . awt . e v e n t . ∗ ; import j a v a x . swing . ∗ ; public c l a s s DemoJKeyFrame extends JFrame implements K e y L i s t e n e r { private JLabel a v i s o = new JLabel ( ” P r e s i o n a r t e c l a s en e l campo de t e x t o : ” ) ; private JLabel e t i q u e t a S a l i d a = new JLabel ( ) ; private J T e x t F i e l d t e x t F i e l d = new J T e x t F i e l d ( 1 0 ) ; public DemoJKeyFrame ( ) { s e t T i t l e ( ”Marco DemoJKeyFrame” ) ; s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE ) ; add ( a v i s o , BorderLayout .NORTH) ; add ( t e x t F i e l d , BorderLayout .CENTER) ; add ( e t i q u e t a S a l i d a , BorderLayout .SOUTH) ; t e x t F i e l d . addKeyListener ( this ) ; } public void keyTyped ( KeyEvent e ) { char c = e . getKeyChar ( ) ; e t i q u e t a S a l i d a . s e t T e x t ( ” Ú l t i m a t e c l a p r e s i o n a d a : ” + c ) ; } public void k e y P r e s s e d ( KeyEvent e ) { } public void k e y R e l e a s e d ( KeyEvent e ) { } public s t a t i c void main ( S t r i n g [ ] a r g s ) { DemoJKeyFrame ventanaKey = new DemoJKeyFrame ( ) ; f i n a l int ANCHO = 2 5 0 ; f i n a l int ALTO = 1 0 0 ; ventanaKey . s e t S i z e (ANCHO, ALTO) ; ventanaKey . s e t V i s i b l e ( true ) ; } } Código 11: Aplicación DemoJKeyFrame. Nota. Los programadores Java llaman a los eventos keyTyped() eventos de “nivel–alto” porque no dependen de la plataforma o el diseño del teclado. En cambio, los eventos keyPressed() y keyReleased() son eventos “nivel–bajo” y dependen de la plataforma y el diseño del teclado Al ejecutar la aplicación DemoJKeyFrame se muestra un indicador en el área del borde norte que pide al usuario teclear en el campo de texto del área central. Con cada tecla presionada, el método keyTyped() cambia la etiqueta en el área de borde sur del marco para mostrar la tecla que fue generada por el KeyEvent más reciente. 22 7. Métodos de la clase AWTEvent Además de los métodos manejadores incluidos con las interfaces receptoras de eventos, las clases AWTEvent por sı́ mismas contienen otros métodos que devuelven información acerca de un evento. Por ejemplo, la clase ComponentEvent tiene el método getComponent() que permite determinar cual de los múltiples Component generó el evento. La clase WindowEvent tiene un método similar, getWindow(), que devuelve la Window que es la fuente de un evento. El cuadro 5 lista algunos métodos útiles para varias clases de eventos. Todos los Component tienen estos métodos: addComponentListener() addFocusListener() addMouseListener() addMouseMotionListener() Se puede llamar cualquiera de los métodos listados en el cuadro 5 usando el formato objeto-puntométodo que se usa con todos los métodos de instancia. Por ejemplo, si se tiene un KeyEvent llamado eventoEntrada y un entero llamado valorUnicode, la siguiente sentencia es válida: valorUnicode = eventoEntrada.getKeyChar(); Cuando se usa un evento, se puede usar cualquiera de los métodos de evento, y por la herencia, se pueden también usar métodos que pertenezcan a cualquier superclase del evento. Por ejemplo, cualquier KeyEvent tiene acceso a los métodos InputEvent, ComponentEvent, AWTEvent, EventObject, y Object, al igual que los métodos KeyEvent. Coordenadas x e y El cuadro 5 refiere las coordenadas x-y de un apuntador de ratón. Una ventana o marco consiste de una cantidad de pı́xeles horizontales y verticales en la pantalla. Cualquier componente que se ponga en la pantalla tiene un posición horizontal, o eje x al igual que una posición vertical, eje y en la ventana. La esquina superior izquierda de cualquier pantalla es la posición 0,0. El primer valor, o coordenada x, se incrementa conforme se viaja de izquierda a derecha a través de la ventana. El segundo valor, coordenada y, se incrementa conforme se va de arriba hacia abajo. 8. Manejo de eventos del ratón La interfaz MouseMotionListener proporciona los métodos llamados mouseDragged() y mouseMoved() que detectan el ratón siendo movido o arrastrando a través de una superficie componente. La interfaz MouseListener da los métodos mousePressed(), mouseClicked(), y mouseReleased() que son análogos a los métodos evento teclado keyPressed(), keyTyped(), y keyReleased() 23 Clase EventObject Método Object getSource() ComponentEvent Component getComponent() WindowEvent Window getWindow() ItemEvent Object getItem() int getStateChange() InputEvent int getModifiers() int getWhen() boolean isAltDown() boolean isControlDown() boolean isShiftDown() KeyEvent getKeyChar() MouseEvent getButton() getClickCount() int getX() int getY() Point getPoint() Propósito Regresa la referencia Object involucrada en el evento. Regresa la referencia Component involucrada en el evento. Regresa la referencia Window involucrada en el evento. Regresa la referencia Object que fue seleccionada o deseleccionada. Regresa un entero llamado ItemEvent.SELECTED o ItemEvent.DESELECTED. Regresa un entero para indicar cual botón del ratón fue pulsado. Regresa un tiempo indicando cuando el evento ocurrió. Regresa si la tecla Alt fue presionada cuando el evento ocurrió. Regresa si la tecla Ctrl fue presionada cuando el evento ocurrió. Regresa si la tecla Shift fue presionada cuando el evento ocurrió. Regresa el carácter Unicode ingresado del teclado. Regresa cual, si hay alguno, de los botones del ratón ha cambido, usa los campos NOBUTTON, BUTTON1, BUTTON2, y BUTTON3. Regresa el número de pulsaciones del ratón; permite identificar las dobles pulsaciones del usuario. Regresal la coordenada x del apuntador del ratón. Regresal la coordenada y del apuntador del ratón. Regresa el objeto Point que contiene las coordenadas x e y del lugar del ratón. Cuadro 5: Métodos útiles de clases de eventos. 24 respectivamente. Sin embargo, con un ratón se está más interesado que sólo el botón presionado; en ocasiones se quiere saber a dónde el ratón apunta. Los métodos adicionales de la interfaz mouseEntered() y mouseExited() informan cuando el usuario posiciona el ratón encima de un componente o mueve el ratón fuera de un componente. La interfaz MouseInputListener implementa todos los métodos de las interfaces MouseListener y MouseMotionListener; sin embargo no tiene métodos propios, pero es conveniente cuando se quiere manejar muchos tipos diferentes de eventos de ratón. Los cuadros 6 y 7 muestran los métodos de las clases MouseListener y MouseMotionListener, respectivamente. Método void mouseClicked(MouseEvent e) void mouseEntered(MouseEvent e) void mouseExited(MouseEvent e) void mousePressed(MouseEvent e) void mouseReleased(MouseEvent e) Descripción Invocado cuando el botón del ratón ha sido pulsado (presionado y soltado) en un componente. Invocado cuando el apuntador del ratón ingresa en un componente. Invocado cuando el apuntador del ratón sale de un componente. Invocado cuando un botón del ratón ha sido presionado en un componente. Invocado cuando un botón del ratón ha sido soltado en un componente. Cuadro 6: Métodos MouseListener. Método void mouseDragged(MouseEvent e) void mouseMoved(MouseEvent e) Descripción Invocado cuando el apuntador del ratón es presionado en un componente y luego arrastrado. Invocado cuando el apuntador del ratón ha sido movido en un componente pero sin algún botón presionado. Cuadro 7: Métodos MouseMotionListener. Nota. La interfaz MouseWheelListener tiene sólo un método llamado mouseWheelMoved(), y este acepta un argumento MouseWheelEvent. Cada uno de los métodos de los cuadros 6 y 7 aceptan un argumento MouseEvent. Un MouseEvent es el tipo de evento generado por la manipulación del ratón. En la siguiente figura se muestra la jerarquı́a de herencia de la clase MouseEvent. De ese diagrama, se puede ver que un MouseEvent es un tipo de InputEvent, el cual es un tipo de ComponentEvent. La clase MouseEvent contiene muchos métodos de instancia y campos que son útiles en describir eventos generados por el ratón. El cuadro 8 lista algunos de los métodos más útiles de la clase MouseEvent, y el cuadro 9 lista algunos campos. 25 java.lang.Object | +--java.util.EventObject | +---java.awt.AWTEvent | +---java.awt.event.ComponentEvent | +---java.awt.event.InputEvent | +---java.awt.event.MouseEvent Método int getButton() int getClickCount() int getX() int getY() Descripción Regresa cual, si hay, de los botones del ratón ha cambiado su estado; usa los campos NOBUTTON, BUTTON1, BUTTON2, y BUTTON3. Regresa la cantidad de pulsaciones del ratón asociado con el evento actual. Regresa la posición-x horizontal del evento relativa al componente fuente. Regresa la posición-y vertical del evento relativa al componente fuente. Cuadro 8: Métodos útiles de la clase MouseEvent. Campo static int BUTTON1 static int BUTTON2 static int BUTTON3 static int NOBUTTON static static static static int int int int MOUSE MOUSE MOUSE MOUSE CLICKED DRAGGED ENTERED EXITED Descripción Indica botón de ratón #1; usado getButton(). Indica botón de ratón #2; usado getButton(). Indica botón de ratón #3; usado getButton(). Indica ningún botón de ratón; usado getButton(). El evento “ratón pulsado”. El evento “ratón arrastrado”. El evento “ratón ingresado”. El evento “ratón salido”. por por por por Cuadro 9: Algunos campos útiles de la clase MouseEvent. La aplicación JFrameMouseAction, código 12, muestra varios de los métodos receptores y de eventos del ratón. JFrameMouseAction extiende JFrame, y como implementa la interfaz MouseListener, debe incluir todos los cinco métodos—mouseClicked(), mouseEntered(), mouseExited(), mousePressed(), y mouseReleased()—a pesar de que no hay acciones incluidas en los métodos mousePressed() y mouseReleased(). 26 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import j a v a . awt . ∗ ; import j a v a . awt . e v e n t . ∗ ; import j a v a x . swing . ∗ ; import s t a t i c j a v a . awt . e v e n t . MouseEvent . ∗ ; public c l a s s JFrameMouseAction extends JFrame implements M o u s e L i s t e n e r { private int x , y ; private JLabel e t i q u e t a = new JLabel ( ” Haciendo a l g o con e l r a t ón” ) ; S t r i n g mensj = ” ” ; public JFrameMouseAction ( ) { s e t T i t l e ( ” A c c i o n e s d e l Ratón” ) ; s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE ) ; s e t L a y o u t (new FlowLayout ( ) ) ; addMouseListener ( t h i s ) ; add ( e t i q u e t a ) ; } public void mouseClicked ( MouseEvent e ) { int cualBoton = e . getButton ( ) ; mensj = ” Se p r e s i o n ó e l ” ; i f ( cualBoton == BUTTON1) mensj += ” botón 1 . ” ; e l s e i f ( cualBoton == BUTTON2) mensj += ” botón 2 . ” ; else mensj += ” botón 3 . ” ; mensj += ” Se e s t á en l a p o s i c i ón ” + e . getX ( ) + ” , ” + e . getY ( ) + ” . ” ; i f ( e . g e t C l i c k C o u n t ( ) == 2 ) mensj += ” Hizo una d o b l e p u l s a c i ón . ” ; else mensj += ” Hizo una p u l s a c i ón s i m p l e . ” ; e t i q u e t a . s e t T e x t ( mensj ) ; } public void mouseEntered ( MouseEvent e ) { mensj = ” Se e n t r ó a l marco . ” ; e t i q u e t a . s e t T e x t ( mensj ) ; } public void mouseExited ( MouseEvent e ) { mensj = ” Se s a l i ó d e l marco . ” ; e t i q u e t a . s e t T e x t ( mensj ) ; } public void mousePressed ( MouseEvent e ) { } public void mouseReleased ( MouseEvent e ) { } public s t a t i c void main ( S t r i n g [ ] a r g s ) { JFrameMouseAction ventana = new JFrameMouseAction ( ) ; f i n a l int ANCHO = 7 5 0 ; f i n a l int ALTO = 3 0 0 ; ventana . s e t S i z e (ANCHO, ALTO) ; ventana . s e t V i s i b l e ( true ) ; } } Código 12: La aplicación JFrameMouseAction. Al ejecutar la aplicación JFrameMouseAction muestra mensaje conforme el usuario genera acciones 27 del ratón. Al inicio de la clase, en la lı́nea 6, dos enteros son declarados para guardar las coordenadas x-y de la posición del ratón. Un JLabel y un String son también declarados, en las lı́neas 7 y 8, para guardar mensajes que informan al usuario de las acciones hechas con el ratón. El constructor, lı́neas 9–15, pone el tı́tulo del JFrameMouseAction, pone la operación de cierra, pone el manejador de diseño, habilita al marco para escuchar los eventos del ratón, y agrega el JLabel al JFrame. La mayorı́a de las acciones ocurren en el método mouseClicked(). En este método se construye un String que al final es asignado al JLabel. Las mismas acciones podrı́an haber sido colocadas en el método mousePressed() o mouseReleased(). Dentro del método mouseClicked(), el objeto MouseEvent llamado e es usado varios veces. Se usa con el método getButton() para determinar cual botón del ratón el usuario ha pulsado, con getX() y getY() para recuperar la posición del ratón donde se pulsó, y con getClickcount() para distinguir entre una pulsación simple y una doble. El usuario es notificado cuando el apuntador del ratón ha “entrado” al JFrame, y también cuando ha “salido” mediante mensajes diferentes generados por los métodos mouseEntered() y mouseExited() respectivamente. El método main() al final de la clase crea una instancia de la clase JFrameMouseAction y fija su tamaño y visibilidad. 9. Uso de menús Los menús son listas de opciones; estas son caracterı́sticas agregadas en programas GUI. Las aplicaciones de usuario son usadas para ver una barra de menú horizontal a lo largo de la parte superior del marco, y se espera que se puedan pulsar estas opciones para producir listas descendentes que muestran más opciones. La lista horizontal de varias JMenu es una JMenuBar. Cada JMenu puede contener opciones, llamadas JMenuItems, o puede contener submenús que también son JMenu. La aplicación JFrameMenu, código 13, muestra un JFrame que muestra el uso de los siguientes componentes: Un JMenuBar que contiene dos JMenu llamados archivo y colores. Tres elementos dentro del JMenu colores: claro, oscuro, blanco. oscuro y blanco son JMenuItems. claro es un JMenu que tiene un submenu. Se puede decir que claro es un submenú porque una flecha está a la derecha de su nombre, y cuando el ratón se pone encima de claro, dos JMenuItem adicionales aparecen: rosa y amarillo. Para generar la salida dada por la ejecución de la aplicación JFrameMenu, un conjunto de objetos JMenuBar, JMenu, y JMenuItem fueron creados y puestos juntos en etapas. Se crean cada uno de los componentes como sigue: 1. Crear un JMenuBar: JMenuBar BarraPrinc = new JMenuBar(); 2. Crear los dos JMenu que son parte del JMenuBar: 28 JMenu archivo = new JMenu("Archivo"); JMenu colores = new JMenu("Colores"); 3. Los tres componentes dentro del JMenu colores son creados como sigue: JMenu claro = new JMenu("Claro"); JMenuItem oscuro = new JMenuItem("Oscuro"); JMenuItem blanco = new JMenuItem("Blanco"); 4. Los dos JMenuItem que son partes del JMenu Claro son creados ası́: JMenuItem rosa = new JMenuItem("Rosa"); JMenuItem amarillo = new JMenuItem("Amarillo"); 5. Después de que los componentes son creados, se ensamblan. Se agrega el JMenuBar a un JFrame usando el método setJMenuBar() como sigue: setJMenuBar(BarraPrinc); Usando el método setJMenuBar() asegura que la barra de menú es anclada a la cima del marco y parece una barra de menú convencional. Observar que el JMenuBar no es agregado al panel de contenido del JFrame; este es agregado al JFrame por sı́ mismo. 6. Los JMenu son agregados al JMenuBar usando el método add(). Por ejemplo: BarraPrinc.add(archivo); BarraPrinc.add(colores); 7. Un submenú y dos JMenuItem son agregados al menú colores como se muestra enseguida: colores.add(claro); colores.add(oscuro); colores.add(blanco); 8. Un submenú puede contener sus propios JMenuItem. Por ejemplo, el JMenu claro que es parte del menú colores contiene sus dos objetos propios JMenuItem: claro.add(rosa); claro.add(amarillo); El código 13 muestra un programa completo que crea un marco con un saludo y el JMenu descrito previamente. 29 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 import j a v a . awt . ∗ ; import j a v a . awt . e v e n t . ∗ ; import j a v a x . swing . ∗ ; public c l a s s JFrameMenu extends JFrame implements A c t i o n L i s t e n e r { private JMenuBar B a r r a P r i n c = new JMenuBar ( ) ; private JMenu a r c h i v o = new JMenu ( ” Archivo ” ) ; private JMenu c o l o r e s = new JMenu ( ” C o l o r e s ” ) ; private JMenuItem s a l i r = new JMenuItem ( ” S a l i r ” ) ; private JMenu c l a r o = new JMenu ( ” C l a r o ” ) ; private JMenuItem o s c u r o = new JMenuItem ( ” Oscuro ” ) ; private JMenuItem b l a n c o = new JMenuItem ( ” Blanco ” ) ; private JMenuItem r o s a = new JMenuItem ( ” Rosa ” ) ; private JMenuItem a m a r i l l o = new JMenuItem ( ” A m a r i l l o ” ) ; private JLabel e t i q u e t a = new JLabel ( ” Hola ” ) ; public JFrameMenu ( ) { s e t T i t l e ( ” Demostración de Menú” ) ; s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE ) ; s e t L a y o u t (new FlowLayout ( ) ) ; setJMenuBar ( B a r r a P r i n c ) ; B a r r a P r i n c . add ( a r c h i v o ) ; B a r r a P r i n c . add ( c o l o r e s ) ; a r c h i v o . add ( s a l i r ) ; c o l o r e s . add ( c l a r o ) ; c o l o r e s . add ( o s c u r o ) ; c o l o r e s . add ( b l a n c o ) ; c l a r o . add ( r o s a ) ; c l a r o . add ( a m a r i l l o ) ; s a l i r . addActionListener ( this ) ; oscuro . addActionListener ( this ) ; blanco . addActionListener ( this ) ; rosa . addActionListener ( this ) ; amarillo . addActionListener ( this ) ; add ( e t i q u e t a ) ; e t i q u e t a . s e t F o n t (new Font ( ” A r i a l ” , Font .BOLD, 2 6 ) ) ; } public void a c t i o n P e r f o r m e d ( ActionEvent e ) { Object s o u r c e = e . g e t S o u r c e ( ) ; C o n t a i n e r con = getContentPane ( ) ; i f ( s o u r c e == s a l i r ) System . e x i t ( 0 ) ; e l s e i f ( s o u r c e == o s c u r o ) con . setBackground ( C o l o r .BLACK) ; e l s e i f ( s o u r c e == b l a n c o ) con . setBackground ( C o l o r .WHITE) ; e l s e i f ( s o u r c e == r o s a ) con . setBackground ( C o l o r . PINK ) ; else con . setBackground ( C o l o r .YELLOW) ; } public s t a t i c void main ( S t r i n g [ ] a r g s ) { JFrameMenu ventana = new JFrameMenu ( ) ; f i n a l int ANCHO = 2 5 0 ; f i n a l int ALTURA = 2 0 0 ; ventana . s e t S i z e (ANCHO, ALTURA) ; ventana . s e t V i s i b l e ( true ) ; } 30 } Código 13: La aplicación JFrameMenu. En la aplicación JFrameMenu, cada JMenuItem se convierte en una fuente para una ActionEvent, y al JFrame se le asigna el papel de receptor para cada uno. El método actionPerformed() determina la fuente de cualquier evento generado. Si el usuario selecciona la opción Salir del menú Archivo, la aplicación termina. Si el usuario selecciona cualquiera de los colores del menú Colores, el color del fondo del JFrame es modificado en consecuencia. Uso de objetos JCheckBoxMenuItem y JRadioButtonMenuItem Las clases JCheckBoxMenuItem y JRadioButtonMenuItem derivan de la clase JMenuItem. Cada una proporciona elementos de menú más especı́ficos como sigue: Los objetos JCheckBoxMenuItem aparecen seguido de una casilla de verificación. Un elemento puede ser seleccionado, mostrando una marca de verificación en la casilla, o no. Se usan los elementos de casilla de verificación para apagar o encender opciones. Los objetos JRadioButtonMenuItem aparecen seguidos de un botón circular de radio. Se espera que los botones de radio sean mutuamente excluyentes, ası́ que se hacen parte de un ButtonGroup. Luego, cuando cualquiera de los botones de radio es seleccionado, todos los otros no quedan seleccionados. El estado de un JCheckBoxMenuItem o un JRadioButtonMenuItem puede ser determinado con el método isSelected(), y se puede modificar el estado de la casilla de verificación con el método setSelected(). La aplicación JFrameMenu2, código 14, muestra dos JCheckBoxMenuItem y tres JRadioButtonMenuItem han sido agregados a un JMenu. A los controles no se les ha asignado todavı́a alguna tarea. 31 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import j a v a . awt . ∗ ; import j a v a . awt . e v e n t . ∗ ; import j a v a x . swing . ∗ ; public c l a s s JFrameMenu2 extends JFrame { private JMenuBar B a r r a P r i n c = new JMenuBar ( ) ; private JMenu menu1 = new JMenu ( ” Archivo ” ) ; private JCheckBoxMenuItem v e r i f i c a 1 = new JCheckBoxMenuItem ( ” Marcar c a j a A” , true ) ; private JCheckBoxMenuItem v e r i f i c a 2 = new JCheckBoxMenuItem ( ” Marcar c a j a B” ) ; private JRadioButtonMenuItem r a d i o 1 = new JRadioButtonMenuItem ( ” Radio o p c i ón 1 ” , true ) ; private JRadioButtonMenuItem r a d i o 2 = new JRadioButtonMenuItem ( ” Radio o p c i ón 2 ” ) ; private JRadioButtonMenuItem r a d i o 3 = new JRadioButtonMenuItem ( ” Radio o p c i ón 3 ” ) ; private ButtonGroup grupo = new ButtonGroup ( ) ; public JFrameMenu2 ( ) { s e t T i t l e ( ” Demostración de Menú s −− 2 ” ) ; s e t D e f a u l t C l o s e O p e r a t i o n ( JFrame . EXIT ON CLOSE ) ; s e t L a y o u t (new FlowLayout ( ) ) ; setJMenuBar ( B a r r a P r i n c ) ; B a r r a P r i n c . add ( menu1 ) ; menu1 . add ( v e r i f i c a 1 ) ; menu1 . add ( v e r i f i c a 2 ) ; menu1 . a d d S e p a r a t o r ( ) ; menu1 . add ( r a d i o 1 ) ; menu1 . add ( r a d i o 2 ) ; menu1 . add ( r a d i o 3 ) ; grupo . add ( r a d i o 1 ) ; grupo . add ( r a d i o 2 ) ; grupo . add ( r a d i o 3 ) ; } 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public s t a t i c void main ( S t r i n g [ ] a r g s ) { JFrameMenu2 ventana = new JFrameMenu2 ( ) ; f i n a l int ANCHO = 3 0 0 ; f i n a l int ALTO = 2 0 0 ; ventana . s e t S i z e (ANCHO, ALTO) ; ventana . s e t V i s i b l e ( true ) ; } } Código 14: Aplicación JFrameMenu2. Uso de addSeparator() Al ejecutar la aplicación JFrameMenu2 y pulsar en la opción Archivo del menú, se observa una lı́nea horizontal que separa en grupos las opciones. Este separador es agregado usando el método addSeparator() en la lı́nea 27 del código 14. El separador no cambia la funcionalidad del menú; simplemente hace el ménu más organizado visualmente para el usuario. 32 Uso de setMnemonic() Un mnemónico ó nemónico es una tecla que causa que un elemento de menú pueda ser escogido por esta. Se puede usar el método setMnemonic() para dar una tecla de menú breve para cualquier elemento visible del menú. Por ejemplo, cuando se agrega la siguiente sentencia al constructor JFrameMenu2, en el código 14, se permite usar una combinación de teclas para acceder la opción: menu1.setMnemonic(’A’); El nemónico para el menú Archivo es puesta a A, ası́ la A en Archivo está subrayada. Cuando un usuario presiona Alt+A en el teclado, el resultado es el mismo si el usuario hubiera pulsado Archivo en el menú: la lista de menú es abierta y mostrada. Se deberán usar diferentes nemónicos para cada elemento del menú que tengan una; si se usa el mismo nemónico varias veces, sólo la primera asignación trabaja. Generalmente, se usa la primera letra de la opción. Si varios elementos del menú inician con la misma letra, la convención es escoger la siguiente letra más prominente en el nombre. Un acelerador es similar a un nemónico. Este es una combinación de teclas que cause que un elemento del menú sea escogido si está o no visible. Por ejemplo, varios programas permiten presionar CTRL+P para imprimir desde cualquier lugar en el programa. Solo elementos menú hoja— menús que no despliegan otros menús—pueden tener aceleradores. Por ejemplo, para asociar el acelerador CTRL+A para la opción del menú “Marcar caja A”, se deberá poner en el constructor de JFrameMenu2 del código 14 lo siguiente: radio1.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_A, Event.CTRL_MASK)); Actividad 2. Crear una aplicación para una empresa de eventos sociales la cual usa una barra de menús con opciones múltples, y que usa varios JPanel separados con diferentes manejadores de diseño para organizar los componentes. 33