import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import java.util.TimerTask; import java.util.Timer; /** * Simple Demo of 1.3 java.util.Timer and TimerTask classes. When * the "Fade" button is pressed we pan an image into view from the * left and right with alternating scan lines coming from opposite * sides of the frame. */ public class FadeDemo { /** * This version of TimerTask just ensures that the the task is * executed on the event dispatching thread. Subclasses must * override doRun() instead of run(). */ private static abstract class SwingTimerTask extends java.util.TimerTask { public abstract void doRun(); public void run() { if (!EventQueue.isDispatchThread()) { EventQueue.invokeLater(this); } else { doRun(); } } } /** * Displays an image and supports the fade operation that we use to * demo java.util.Timer/TimerTask. */ private static class ImagePanel extends JPanel { private final Timer timer = new Timer(); private final Image image; private final int imageWidth; private final int imageHeight; private int[] scanLineOffsets; private long startTime = -1; private long totalTime = -1; private long frameRate = 0; /** * Create an ImagePanel that will display the specified image. */ ImagePanel(String file) { super(null); // null layout manager this.image = new ImageIcon(file).getImage(); imageWidth = image.getWidth(this); imageHeight = image.getHeight(this); scanLineOffsets = new int[imageWidth]; setBackground(Color.black); } /** * Return the image size. */ public Dimension getPreferredSize() { Insets insets = getInsets(); int width = imageWidth + insets.left + insets.right; int height = imageHeight + insets.top + insets.bottom; return new Dimension(width, height); } /** * Draw the image one scan line at a time. Each scan-line is drawn * at offset[y] - the pan method takes care of setting the offsets * before calling repaint(). */ public void paintComponent(Graphics g) { super.paintComponent(g); /* If we're in the middle of a fade, compute what percentage * of the overall image width should be displayed now, update * the offsets appropriately. */ if ((startTime > 0) && (totalTime > 0)) { long dt = System.currentTimeMillis() - startTime; int dw = (int)((double)imageWidth * ((double)dt / (double)totalTime)); for(int r = 0; r < scanLineOffsets.length; r += 2) { scanLineOffsets[r] = Math.min(0, dw - imageWidth); if ((r + 1) < scanLineOffsets.length) { scanLineOffsets[r + 1] = Math.max(0, -scanLineOffsets[r]); } } } /* Paint the image, one scan line at a time. */ Insets insets = getInsets(); for(int r = 0; r < imageHeight; r++) { int dx1 = insets.left + scanLineOffsets[r]; int dy1 = insets.top + r; int dx2 = dx1 + imageWidth; int dy2 = dy1 + 1; int sx1 = 0; int sy1 = r; int sx2 = sx1 + imageWidth; int sy2 = sy1 + 1; g.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, this); } } /** * The frame rate is based on an estimate of the time it takes to paint the * frame. Our estimate is based on the average elapsed time required to * paint the frame to an offscreen image plus 10%. The extra 10% is * to try and guard against making the frame rate so fast that * slight variations in the performance of paintComponent will make the * TimerTask fall behind. */ public long computeFrameRate() { Graphics g = createImage(imageWidth, imageHeight).getGraphics(); long dt = 0; paintComponent(g); for(int i = 0; i < 10; i++) { long startTime = System.currentTimeMillis(); paintComponent(g); dt += System.currentTimeMillis() - startTime; } return (long)((float)(dt / 10) * 1.1f); } /** * Fade the image in over totalFadeTime milliseconds. *

* Compute the frame rate, if it hasn't been computed already, and then * schedule a TimerTask that will call repaint() at that rate. * Each time the TimerTask fires the paintComponent method figures out what * percentage of the total time has elapsed and then adjust the * scan line offsets to match. When we're out of time, the TimerTask * is cancelled. */ public void startFade(long totalFadeTime) { SwingTimerTask updatePanTask = new SwingTimerTask() { public void doRun() { /* If we've used up the available time then cancel * the timer. */ if ((System.currentTimeMillis() - startTime) >= totalTime) { endFade(); cancel(); } repaint(); } }; totalTime = totalFadeTime; startTime = System.currentTimeMillis(); if (frameRate == 0) { frameRate = computeFrameRate(); } timer.schedule(updatePanTask, 0, frameRate); } /** * Clear the internal state associated with a fade. */ public void endFade() { totalTime = startTime = -1; for(int r = 0; r < scanLineOffsets.length; r++) { scanLineOffsets[r] = 0; } } } public static void main(String[] args) { final ImagePanel imagePanel = new ImagePanel("images/duck.jpg"); final JSlider totalTimeSlider = new JSlider(500, 5000, 2000); totalTimeSlider.setMajorTickSpacing(1000); totalTimeSlider.setMinorTickSpacing(100); totalTimeSlider.setPaintTicks(true); totalTimeSlider.setPaintLabels(true); Action panAction = new AbstractAction("Fade") { public void actionPerformed(ActionEvent e) { imagePanel.startFade((long)(totalTimeSlider.getValue())); } }; JToolBar toolbar = new JToolBar(); toolbar.add(panAction); toolbar.addSeparator(); toolbar.add(new JLabel("Total Fade Time (ms): ")); toolbar.add(totalTimeSlider); /* Application boilerplate. */ JFrame f = new JFrame("Timer/TimerTask Demo"); WindowListener l = new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }; f.addWindowListener(l); Container contentPane = f.getContentPane(); contentPane.add(imagePanel, BorderLayout.CENTER); contentPane.add(toolbar, BorderLayout.NORTH); f.pack(); f.setVisible(true); } }