001package myproject.util; 002 003import java.awt.Color; 004import java.awt.Dimension; 005import java.awt.Graphics; 006import java.util.Observable; 007import javax.swing.JFrame; 008import javax.swing.JPanel; 009import javax.swing.SwingUtilities; 010import javax.swing.WindowConstants; 011 012/** 013 * A swing implementation of {@link Animator}, using a {@link JFrame} 014 * to display the animation. The {@link JFrame} is created and 015 * displayed by the constructor. 016 * 017 * Calls to <code>update()</code> result in a call to 018 * <code>painter.paint()</code>. This is executed in the swing 019 * thread while the main thread is paused for <code>delay</code> 020 * milliseconds. 021 */ 022public class SwingAnimator implements Animator { 023 // The following fields are manipulated by the main program thread 024 private int delay; 025 026 // The following fields are manipulated by the swing thread 027 private JFrame frame; // Swing representation of an OS window 028 private ContentPane content; // A paintable component 029 private boolean disposed = false; // If true, then die 030 031 /** 032 * Creates and displays a {@link JFrame} for the animation. 033 * @param name The name to be displayed on the graphical window. 034 * @param width The width of the display, in pixels. 035 * @param height The height of the display, in pixels. 036 * @param delay Time to pause after an update, in milliseconds. 037 */ 038 public SwingAnimator(final SwingAnimatorPainter painter, final String name, final int width, final int height, int delay) { 039 this.delay = delay; 040 // Create a graphics window and display it 041 SwingUtilities.invokeLater(() -> { 042 content = new ContentPane(painter, width, height); // A paintable component for content 043 frame = new JFrame(); // An OS window 044 frame.setTitle(name); // The title of the Frame 045 frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); // End program if Frame is closed 046 frame.setContentPane(content); // Associate the content with the Frame 047 frame.pack(); // Fix the layout of the Frame 048 frame.setVisible(true); // Display the Frame 049 }); 050 } 051 052 /** 053 * Throw away this visualization. 054 */ 055 public void dispose() { 056 SwingUtilities.invokeLater(() -> { 057 frame.dispose(); 058 disposed = true; 059 }); 060 } 061 062 /** 063 * Calls to <code>update</code> are executed in the swing thread, 064 * while the main thread is paused for <code>delay</code> 065 * milliseconds. 066 */ 067 public void update(final Observable model, Object ignored) { 068 if (disposed) 069 throw new IllegalStateException(); 070 071 // Redraw the window 072 // content.repaint() causes a call to content.paint(g) 073 // where g is an appropriate graphics argument. 074 SwingUtilities.invokeLater(() -> content.repaint()); 075 076 // Delay the main thread 077 try { 078 Thread.sleep(delay); 079 } catch (InterruptedException e) {} 080 } 081 082 /** 083 * A component for painting. 084 * All code is executed in the swing thread. 085 */ 086 private static class ContentPane extends JPanel { 087 private static final long serialVersionUID = 2008L; 088 private int width; 089 private int height; 090 private SwingAnimatorPainter painter; 091 092 ContentPane(SwingAnimatorPainter painter, int width, int height) { 093 this.painter = painter; 094 this.width = width; 095 this.height = height; 096 setPreferredSize(new Dimension(width, height)); 097 setDoubleBuffered(true); 098 setOpaque(true); 099 setBackground(Color.WHITE); 100 } 101 102 public void paint(Graphics g) { 103 // This test is necessary because the swing thread may call this 104 // method before the simulation calls SwingAnimator.update() 105 if (painter != null ) { 106 // The clearRect is necessary, since JPanel is lightweight 107 g.clearRect(0, 0, width, height); 108 painter.paint(g); 109 } 110 } 111 } 112}