import java.awt.*; import java.awt.event.*; import javax.swing.*; /** a Java Swing application using slight "animation" via a thread @author Sharon Tuttle @version 2021-10-25 */ public class ThreadImage1 { /** creates a simple frame with a panel that can have an image animated across it @param args is expected to contain a string containing the name of a .gif or .jpg or .png image file */ public static void main(String[] args) { if (args.length != 1) { JOptionPane.showMessageDialog(null, "Need 1 argument, " + "a .gif or .jpg or .png file name"); System.exit(1); } // quirk: I may need this so an inner class can see it...? final String imageFileName = args[0]; EventQueue.invokeLater( () -> { ThreadImage1Frame mainFrame = new ThreadImage1Frame(imageFileName); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mainFrame.setVisible(true); } ); } } /** A frame with a panel that can have an image animated across it */ class ThreadImage1Frame extends JFrame { // data fields private static final int DEFAULT_WIDTH = 450; private static final int DEFAULT_HEIGHT = 200; /** construct a ThreadImage1Frame instance @param imgFileName the name of a .gif or .png or .jpg image file */ public ThreadImage1Frame(String imgFileName) { setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); setTitle("Animation of YOUR image"); ThreadImage1Panel mainPanel = new ThreadImage1Panel(imgFileName, DEFAULT_WIDTH, DEFAULT_HEIGHT); add(mainPanel); } } /** A panel whose subpanel can have a moving image, provided by the user */ class ThreadImage1Panel extends JPanel { // data fields! private final static Font DISPLAY_FONT = new Font("Dialog", Font.PLAIN, 20); private String imgName; private ImageIcon myImageIcon; private int imageX; private int imageY; private int displayWidth; private int displayHeight; private Thread paintThread; private Runnable myPaintIt; /** set up display parameters and a Runnable instance for eventually animating a provided image @param imgFile the name of a .gif or .jpg ot .png file @param width desired display widthm in pixels @param height desired display height, in pixels */ public ThreadImage1Panel(String imgFile, int width, int height) { myPaintIt = null; paintThread = null; setLayout(new BorderLayout()); imgName = imgFile; // shorter, to compensate for a begin-and-end-buttons-panel // in the South... MyImagePanel aMyImagePanel = new MyImagePanel(width, height - 20); add(aMyImagePanel, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); JButton begin = new JButton("Begin"); begin.setFont(DISPLAY_FONT); begin.addActionListener(new BeginThread()); buttonPanel.add(begin); JButton end = new JButton("End"); end.setFont(DISPLAY_FONT); end.addActionListener(new EndThread()); buttonPanel.add(end); add(buttonPanel, BorderLayout.SOUTH); } /** this subpanel will be painted upon!! */ private class MyImagePanel extends JPanel { /** construct a new MyImagePanel @param width my new MyImagePanel's desired displayWidth @param height my new MyImagePanel's desired displayHeight */ public MyImagePanel(int width, int height) { displayWidth = width; displayHeight = height; // start out the image with its top left corner at (0, 0) imageX = 0; imageY = 0; // one way to create an ImageIcon: call its constructor // that expects them name of a .png, .jpg, or .gif // image file myImageIcon = new ImageIcon(imgName); myPaintIt = () -> { boolean finished = false; while (!finished) { try { if (imageX < displayWidth) { imageX += 5; } else { imageX = 0; } repaint(); Thread.sleep(100); } catch (InterruptedException exc) { finished = true; } } }; // NOT creating paintThread yet -- will do when the Begin // button is clicked, so I can re-create it MORE than once! } /** paints/draws the provided image on this panel */ public void paintComponent(Graphics g) { super.paintComponent(g); // an ImageIcon can be painted on this panel using // its paintIcon method myImageIcon.paintIcon(this, g, imageX, imageY); } } /** action listener to create a new thread (if appropriate) when Begin button is clicked */ private class BeginThread implements ActionListener { /** begin a new thread (if appropriate) when Begin button is pushed @param event not used here */ public void actionPerformed(ActionEvent event) { // if paintThread is not currently running, create // a new thread and start it up; othwerwise, // ignore this button push if (paintThread == null) { paintThread = new Thread(myPaintIt); paintThread.start(); } } } /** action listener to end the image-moving thread (if appropriate) when End button is clicked */ private class EndThread implements ActionListener { /** end the image-moving thread (if appropriate) when End button is pushed @param event not used here */ public void actionPerformed(ActionEvent event) { // only do something IF there is currently a // paintThread running -- othwerwise, // ignore this button push if (paintThread != null) { paintThread.interrupt(); paintThread = null; } } } }