martes, 27 de septiembre de 2011

Reloj análogico con Swing, ejemplo Swing Timer

1 comentarios
Hoy profundizaremos un poco en el Swing Timer, una instancia de Timer dispara una o varias acciones después de un retardo especificado.



Se recomienda el uso de Swing timers para la realización de tareas relacionadas con la interfaz de usuario (GUI-task) ya que todos los timers comparten un mismo hilo y todas las tareas se ejecutan automáticamente en el hilo de despacho de eventos (event-dispatch thread).



Su uso es muy fácil, cuando creamos el timer, especificamos un action listener que se notificará cuando el temporizador acaba. Es válido tanto para tareas repetitivas como para realizar una sola tarea transcurrido un cierto retraso, para este último fin podremos invocar setRepeats(false) en el timer.

Para inciarlo se llama a su método start(), para suspenderlo llamamos a stop().



//Timer
	Timer timer = new Timer(TIMER_UPDATE, new ActionListener() {
		public void actionPerformed(ActionEvent e) {
			// Tareas repetitivas...
			}
		});

timer.start();


Como ejemplo de uso de un Swing timer, les presento la implementación de un reloj analógico hecho con Swing.



Este sería el resultado y aquí a continuación os dejo el código.




En primer lugar nuestra clase reloj que definirá y arrancará el Timer.

package reloj;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;

import javax.swing.JPanel;
import javax.swing.Timer;


public class Reloj extends JPanel {

	/** Tiempo de actualizacion. */
	private static final int TIMER_UPDATE = 1000;

	/** Calendar. */
	private Calendar calendar = null;

	/**
	 * Constructor.
	 */
	public Reloj() {

		setUI(new RelojUI());

		//Inicializamos el calendario
		calendar = Calendar.getInstance();

		//Establecemos el tamaño a nuestro panel
		setPreferredSize(new Dimension(295, 270));

		//Timer para actualizar el calendario
		Timer timer = new Timer(TIMER_UPDATE, new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				//Añadimos 1 segundo al calendario
				calendar.add(Calendar.SECOND, 1);
				// repintamos
				repaint();
			}
		});
		timer.start();
	}

	/**
	 * Recupera la instancia de Calendar
	 * @return calendar
	 */
	public Calendar getCalendar() {
		return this.calendar;
	}

}
Con cada iteración del Timer llamamos al método repaint() de nuestro reloj, repintando así las manecillas de acuerdo a la hora actual que nos devolvera la instancia de Calendar que hemos definido y actualizaremos también con cada iteración.

A continuación el UI del reloj, que se encargará de redibujarlo cada segundo según establecimos en el retardo del Timer.



package reloj;

import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.net.URL;
import java.util.Calendar;

import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.PanelUI;

public class RelojUI extends PanelUI {

	private ImageIcon backgroundImage = null;

	private BasicStroke handsStroke;

	public static ComponentUI createUI(JComponent c) {
		return new RelojUI();
	}

	@Override
	public void installUI(JComponent c) {
		super.installUI(c);
		//Hacemos el panel transparente
		c.setOpaque(false);
		backgroundImage = createImage("imagenes/reloj.jpg");
		// pincel que utilizaremos para las manecillas de los minutos y horas
		handsStroke = new BasicStroke(2f);
	}

	@Override
	public void paint(Graphics g, JComponent c) {

		Graphics2D g2d = (Graphics2D) g;
		// imagen de fondo
		if (backgroundImage != null) {
			g2d.drawImage(backgroundImage.getImage(), 0, 0, null);
		}
		Reloj reloj = (Reloj) c;
		int hour = reloj.getCalendar().get(Calendar.HOUR);
		int minute = reloj.getCalendar().get(Calendar.MINUTE);
		int second = reloj.getCalendar().get(Calendar.SECOND);

		// acotamos la parte donde dibujaremos las manecillas del reloj
		Shape oval = new Ellipse2D.Double(0, 0, 167, 164);
		int centerX = (int) (oval.getBounds().getWidth() / 2);
		int centerY = (int) (oval.getBounds().getHeight() / 2);

		// puntos donde acaban las manecillas del reloj
		int xh, yh, xm, ym, xs, ys;

        // calculamos la posicion de las manecillas del reloj
		xs = (int) (Math.cos(second * Math.PI / 30 - Math.PI / 2) * 75 + centerX);
        ys = (int) (Math.sin(second * Math.PI / 30 - Math.PI / 2) * 75 + centerY);
        xm = (int) (Math.cos(minute * Math.PI / 30 - Math.PI / 2) * 60 + centerX);
        ym = (int) (Math.sin(minute * Math.PI / 30 - Math.PI / 2) * 60 + centerY);
        xh = (int) (Math.cos((hour * 30 + minute / 2) * Math.PI / 180 - Math.PI / 2) * 45 + centerX);
        yh = (int) (Math.sin((hour * 30 + minute / 2) * Math.PI / 180 - Math.PI / 2) * 45 + centerY);

		//offset, movemos el origen de las coordenadas usadas para dibujar
        //al vertice superior izquierdo del rectángulo que contiene la esfera del reloj.
		g.translate(48, 78);

		//dibujamos los numeros y las manecillas
		g.drawString("9", 10, centerY+5);
        g.drawString("3", (int) (oval.getBounds().getWidth() - 10), centerY+5);
        g.drawString("12", centerX-6, 20);
        g.drawString("6", centerX-3, (int) (oval.getBounds().getHeight() - 10));

		// segundos
        g.drawLine(centerX, centerY, xs, ys);
        // minutos
        g2d.setStroke(handsStroke);
        g.drawLine(centerX, centerY, xm, ym);
        //horas
        g.drawLine(centerX, centerY, xh, yh);

        // - offset
		g.translate(-47, -78);

	}

	/**
	 * Para recuperar una imagen de un archivo...
	 * @param path Ruta de la imagen relativa al proyecto
	 * @return una imagen
	 */
	public ImageIcon createImage(String path) {
		URL imgURL = getClass().getResource(path);
	    if (imgURL != null) {
	        return new ImageIcon(imgURL);
	    } else {
	        System.err.println("Couldn't find file: " + path);
	        return null;
	    }
	}

}
Y por último la clase main que lanzará la aplicación.



package reloj;

import javax.swing.JFrame;


public class Main {

	private static void createAndShowGUI() {
		JFrame frame = new JFrame("Reloj con Swing");

		Reloj reloj = new Reloj();

	    frame.getContentPane().add(reloj);
	    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	    frame.setSize(302, 302);
	    frame.setVisible(true);
	}

	public static void main(String[] args) {

		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createAndShowGUI();
			}
		});
	}
}

Un saludo y hasta la próxima!