1.00 Clase 17 Introducción a la API de gráficos 2D de Java Anuncios • Boletín de problemas 5: compruebe la web – En los problemas 1 y 2, no es necesario que utilice clases internas anónimas si prefiere utilizar getSource() en sus métodos actionPerformed(), para determinar el origen del evento. – En el problema 2, hemos decidido facilitarle una de las dos clases que le pedimos que escribiese BusDrawing. La puede descargar de la web. Si lo desea, también puede utilizar su versión. 2 1 Los orígenes de la API de gráficos 2D de Java • La herramienta original de la GUI de Java, la AWT, era una solución rápida y poco fiable. Utilizaba componentes de pares nativos para dibujar todos los controles. • Swing dibuja todos los componentes excepto los contenedores de alto nivel, que utilizan los métodos de Java en vez de basarse en controles específicos de la plataforma. • Para hacer esto, Swing necesitaba mejores gráficos. • La API 2D de Java surgió como tecnología instrumental para Swing. • Existe también ahora una API 3D de Java. • Consulte el tutorial en: http://java.sun.com/docs/books/tutorial/2d/index.html 3 Arquitectura gráfica de Java Swing AWT para Java API de gráficos 2D de Java Sistema de ventanas de la plataforma Arquitectura de gráficos de la plataforma 4 2 NgonApp Diseño personalizado Componentes de Swing: JLabel y JTextField 5 ¿En qué momentos se dibuja una GUI? • En la pantalla de inicio (no necesariamente cuando el programa se arranca). • Cuando la visualización varía. Por ejemplo, cuando la ventana o parte de ella queda oculta tras otra y luego vuelve a mostrarse. • Cuando el contenido cambia, y Swing o el programador piden una actualización (repaint()). 6 3 ¿Cómo se dibuja una GUI? • Swing programa el diseño. Es posible que combine múltiples peticiones de rediseño que se sucedan rápidamente. • Swing llama a los tres métodos siguientes en orden: paintComponent() paintBorder() paintChildren() • El último pinta recursivamente los hijos de un contenedor. 7 Diseño personalizado • Los componentes estándar de Swing, tales como JLabel y JComboBox, a sí mismos. utilizan el paintComponent() para dibujarse • Si desea crear un diseño personalizado, amplíe una clase contenedora, normalmente JPanel , y anule a paintComponent () . • Utilice llamadas de la API 2D en paintComponent() para dibujar lo que desee en el fondo de JPanel. • Anule getPreferredSize() o llame a setPreferredSize () a su diseño. para ajustar el tamaño de JPanel 8 4 La clase Graphics • Se llama a paintComponent () con el argumento Graphics g, que sirve como como herramienta de dibujo inicializada a los valores por defecto del componente. • El argumento es realmente un objeto Graphics2D. Por tanto, utilícelo. Graphics era, originariamente, la clase de AWT. • La API 2D arranca normalmente del modo siguiente: public void paintComponent ( Graphics g ) { super.paintComponent( g ); Graphics2D g2 = (Graphics2D) g; // los comandos de dibujo van aquí } 9 ¿De dónde procede el argumento Graphics? • El argumento Graphics del método paintComponent() es una captura de pantalla de los valores gráficos por defecto del componente, al igual que la fuente y el color del diseño en el momento en que se llama a los métodos de dibujo (paint). • Es sólo una copia de estos valores. Cada vez que se llama a los métodos de dibujo, se obtiene una nueva versión de Graphics. Ninguno de los cambios realizados a una instancia de Graphics en una llamada a paintComponent(), se recordará la siguiente vez que se llame al método; y ningún cambio como, por ejemplo, el realizado con setFont() se transmitirá al componente en sí. • es 10 5 Operaciones básicas de la API 2D Puede usar el argumento Graphics2D para: 1. 2. dibujar contornos de figuras utilizando el método public void draw( Shape s ) dibujar figuras con relleno utilizando el método public void fill( Shape s ) Puede usar el argumento de Graphics o Graphics2D para: 3. 4. dibujar una imagen utilizando uno de los métodos public void drawImage( . . . ) dibujar una cadena de texto utilizando los métodos public void drawString( . . . ) 11 Contexto de representación de gráficos 2D Gran parte del poder de la API 2D proviene de la habilidad del usuario para definir los atributos del objeto Graphics2D, lo que se conoce como contexto de representación (rendering context): – public void setStroke(Stroke s) – public void setPaint(Paint p) – – – – public public public public void void void void setFont(Font f) setComposite(Composite c) setTransform(Transform t) setRenderingHints(Map m) 12 6 Plantilla de diseño personalizado import java.awt.*; // para Graphics2D, Paint, Shape, … import java.awt.geom.*; // para clases Shape concretas import javax.swing.*; // para JPanel, etc public class MyPanel extends JPanel { . . . public void paintComponent( Graphics g ) { super.paintComponent( g ); Graphics2D g2 = (Graphics2D) g; . . . g2.setPaint/Stroke/Font/etc(...); Shape s = new Shape2D.Float/Double( ... ); g2.draw/fill( s ); . . . 13 NgonApp, Primera versión JFrame con BorderLayout CENTRO CON NgonView amplía a JPanel JPanel con FlowLayout SUR 14 7 NgonApp, versión 1, 1 import javax.swing.*; import java.awt.*; import java.awt.event.*; public class NgonApp1 extends JFrame { private NgonView1 view; private JTextField nField; public NgonApp1() { super( "NgonApp" ); setDefaultCloseOperation( EXIT_ON_CLOSE ); view = new NgonView1(); getContentPane().add( view, BorderLayout.CENTER ); 15 NgonApp, versión 1, 2 JPanel nPanel = new JPanel(); Font labelFont = new Font( "SansSerif", Font.BOLD, 18 ); JLabel nLabel = new JLabel( "Número de lados = " ); nLabel.setFont( labelFont ); nPanel.add( nLabel ); nField = new JTextField( 5 ); nField.setFont( labelFont ); nField.addActionListener(new ActionListener(){...}); nPanel.add( nField ); getContentPane().add( nPanel, BorderLayout.SOUTH ); pack(); } 16 8 NgonApp, versión 1,3 el ActionListener nField.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { int nSides; String nStr = nField.getText(); try { nSides = Integer.parseInt( nStr ); view.setSides( nSides ); } catch ( NumberFormatException n ) { view.setSides( 0 ); } nField.selectAll(); } // Fin de actionPerformed } ); // Fin de addActionListener 17 NgonView , versión 1, 1 import javax.swing.*; import java.awt.*; import java.awt.geom.*; public class NgonView1 extends JPanel { private int nSides = 0; private Font ngonFont; static public final Dimension PREF_SIZE = new Dimension( 600, 700 ); static public final int warnX = 180; static public final int warnY = 600; static public final int areaX = 180; static public final int areaY = 600; 18 9 NgonView , versión 1, 2 public NgonView1() { ngonFont = new Font("SansSerif", Font.ITALIC, 16); } public void setSides ( int n ) { nSides = n; repaint(); } public Dimension getPreferredSize() { return PREF_SIZE; } private double getArea() { double PIn = Math.PI / nSides; return nSides * Math.cos( PIn ) * Math.sin( PIn ); } 19 NgonView , versión 1, 3 Texto en el diseño public void paintComponent( Graphics g ) { Graphics2D g2 = (Graphics2D) g; super.paintComponent( g ); g2.setFont( ngonFont ); g2.setPaint( Color.red ); if ( nSides < 3 ) { g2.drawString( "Elija un número de lados >= 3", warnX, warnY ); } else { g2.drawString( "Área del polígono = " + getArea(), areaX, areaY ); } 20 10 NgonView , versión 1 Ubicar el círculo 100 100 400 400 21 NgonView , versión 1, 3 Dibujar el círculo g2.setStroke( new BasicStroke( 2 ) ); g2.setPaint( Color.blue ); Shape circle = new Ellipse2D.Float( 100, // superior izquierda x 100, // superior izquierda y 400, // ancho del cuadrado que lo circunscribe 400 ); // altura de dicho cuadrado g2.draw( circle ); } 22 11 Sistema de coordenadas de Graphics2D • El objeto Graphics2D utiliza un tipo de coordenadas reales (en vez de coordenadas de dispositivo) que Java denomina espacio del usuario. • Las formas y operaciones de Graphics2D están definidas como coma flotante (float o double) pero la coordenada Y aumenta hacia abajo. • Algunas llamadas Graphics2D sólo admiten floats. • Las coordenadas de coma flotante están diseñadas para garantizar la independencia de sus gráficos con respecto al dispositivo de salida. • La API 2D está diseñada para mostrar la salida en pantalla a diferentes resoluciones. L 23 La transformación por defecto de Graphics2D • El proyecto de representación 2D aplica una transformación geométrica para mapear el espacio del usuario con el espacio del dispositivo. • La transformación por defecto mapea unidades 1.0 de espacio de usuario con ~1/72 de pulgada, que resulta ser el tamaño medio por píxel en una pantalla o el tamaño de un punto en una impresora. • De modo que a menos que haga algo especial, el diseño 2D utilizará por defecto las coordenadas en píxel. 24 12 NgonView , versión 2 Utilizando transformación de coordenadas (-1.0,-1.0) Y 1.0 X (0.0,0.0) 1.0 = 200 pixels 25 NgonView , versión 2, Transformación de coordenadas, 1 static public final float SCALE = 200.0F; static public final float tx = 1.5F; static public final float ty = 1.5F; private float transX( float x ) { return ( x + tx ) * SCALE; } private float transY( float y ) { return ( y + ty ) * SCALE; } 26 13 NgonView , versión 2, Transformación de coordenadas, 2 public void paintComponent( Graphics g ) { . . . g2.setStroke( new BasicStroke( 2 ) ); Shape circle = new Ellipse2D.Float( transX ( -1.0F ), transY( -1.0F ), 2.0F * SCALE, 2.0F * SCALE ); g2.setPaint( Color.yellow ); g2.fill( circle ); g2.setPaint( Color.blue ); g2.draw( circle ); } 27 NgonView , versión 3 Cómo rellenar el polígono θ = 2π / 5 θ ( 0.0,0.0 ) (1.0,0.0 ) ( cos θ ,sin θ ) 28 14 Construcción de formas más complejas • Puede crear formas más complejas mediante la agrupación de formas y rutas (segmentos de línea, curvas cuadráticas y curvas cúbicas). • El Shape combinado es un GeneralPath, que implementa la interfaz Shape para que usted pueda dibujarla o rellenarla como cualquier otra forma. • Para saltar a otro punto, dejando un trozo sin dibujar, utilice: moveTo( float x, float y ); • Para dibujar una línea que siga una ruta, utilice: lineTo( float x, float y ); 29 NgonView , versión 3, Cómo rellenar el polígono, 2 public void paintComponent( Graphics g ) { . . . float dtheta = (float) ( 2 * Math.PI ) / nSides; g2.setPaint( Color.green ); GeneralPath ngon = new GeneralPath(); ngon.moveTo( transX( 1.0F ), transY( 0.0F ) ); for ( int i = 1; i < nSides; i++ ) { float x = transX( (float) Math.cos( i*dtheta )); float y = transY( (float) Math.sin( i*dtheta )); ngon.lineTo( x, y ); } ngon.closePath(); g2.fill( ngon ); } 30 15