In yesterday's post we took our first, tentative steps into the world of Java GUIs with AWT and Swing.
WTF! Too many
TLAs (
Three
Letter
Acronyms). Let's back up a little and address each acronym in turn.
GUI : a
Graphical
User
Interface - think of the difference between Windows and DOS (if you're old enough to remember DOS). A GUI usually consists of windows, mouse, buttons, icons and so on.
AWT : the
Abstract
Window
Toolkit. In other languages, we'd call AWT a
library of routines. In Java, it's better to think of it as a
toolkit providing class definitions for windowing, graphics and user-interface "widget" objects. AWT interacts with the host operating System (OS) - Windows, Mac or Linux - at a low level. Because of this, a window opened using an AWT class will look like a Windows window when the program is run on a Windows PC, and like an OS X window when the program is run on a Mac.
Swing : a GUI widget toolkit kind of built on top of (and beside) AWT, that gives you more control over the look and feel of your applications.
So, yesterday's program defined "ShowImageApp" as an extension of the Swing "JPanel" class. Our program's "main" method constructed a new "showPanel" (the contents of our program's window) and stuffed it into a newly constructed Swing "JFrame" object called "showFrame" (the frame of the window).
Today, we're going to modify our program to load a second image, and to use an AWT timer to switch between the two images at one second intervals.
Let's start with our second image - everything's cuter with hearts:
When you've finished BAAAAWWWW-ing, save this image as "hitler_bambi_1.jpg"
in the same place you stored "hitler_bambi_0.jpg" yesterday.
First, we need to import definitions of timer-related objects:
import java.awt.event.*;
We need a new constant to define the period (in milliseconds) between picture changes.
private static final int PERIOD = 1000;
Now, we change our declaration of variable "image" to an array "images".
private BufferedImage [] images = new BufferedImage [2];
We also need a variable to keep track of which image from our array we should be displaying.
private int imageIndex;
In the "ShowImageApp()" constructor method, we change the initialization code to:
public ShowImageApp()
{
setPreferredSize(new Dimension(PWIDTH,PHEIGHT));
images [0] = loadImage("Images/hitler_bambi_0.jpg");
images [1] = loadImage("Images/hitler_bambi_1.jpg");
imageIndex = 0;
new Timer(PERIOD, this).start(); // start the Swing timer
} // ShowImageApp
This loads both images, sets the image index to point at the first (0th!) image, and starts a Timer with period 1000ms.
The "paintComponent()" is modified slightly to display the current image.
public void paintComponent(Graphics g)
{
g.drawImage(images [imageIndex],0,0,this);
}
The only other change we need to make is to tell the program what to do every time the Timer "goes off". For that we define an "actionPerformed" method as follows.
public void actionPerformed(ActionEvent e)
{
repaint();
imageIndex = (imageIndex + 1) % 2;
}
So, each time the Timer period expires, the "actionperformed" method is called. The method calls "repaint()" to trigger our "paintComponent()" method. The "imageIndex" is then toggled between 0 and 1.
One last change is to the line introducing the "ShowImageApp" class - we need to tell the compiler that "ShowImageApp" objects will listen for Timer actions:
public class ShowImageApp extends JPanel implements ActionListener
In Java jargon, the class
implements the
abstract interface "ActionListener".
Adding all of the above changes to yesterday's program we get:
// ShowImageApp.java
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import javax.imageio.*;
import java.io.*;
public class ShowImageApp extends JPanel implements ActionListener
{
private static final int PERIOD = 1000; // Swing timer interval 1.0 secs
private static final int PWIDTH = 417;
private static final int PHEIGHT= 400;
private BufferedImage [] images = new BufferedImage [2];
private int imageIndex;
public ShowImageApp()
{
setBackground(Color.black);
setPreferredSize(new Dimension(PWIDTH,PHEIGHT));
images [0] = loadImage("Images/hitler_bambi_0.jpg");
images [1] = loadImage("Images/hitler_bambi_1.jpg");
imageIndex = 0;
new Timer(PERIOD, this).start(); // start the Swing timer
} // end of constructor
public BufferedImage loadImage(String fileName)
{
try {
BufferedImage im = ImageIO.read(getClass().getResource(fileName));
return im;
} catch(IOException e) {
System.out.println("Load Image error for "+fileName+":\n"+e);
return null;
}
}
// triggered by the timer: repaint and toggle the image index
public void actionPerformed(ActionEvent e)
{
repaint();
imageIndex = (imageIndex + 1) % 2; // toggle between 0 and 1
}
public void paintComponent(Graphics g)
{
g.drawImage(images [imageIndex],0,0,this);
}
public static void main(String args[])
{
final ShowImageApp showPanel = new ShowImageApp();
JFrame showFrame = new JFrame("Show Images");
showFrame.getContentPane().add(showPanel,BorderLayout.CENTER);
showFrame.setDefaultCloseOperation (WindowConstants.EXIT_ON_CLOSE);
showFrame.pack();
showFrame.setResizable(false);
showFrame.setVisible(true);
} // main
} // ShowImageApp Class
Save the program as
"ShowImageApp.java" and compile and run in the same way as yesterday and you should get the cutest flashing image
ever.
"Big deal" I hear you say, "we could have done that with a .GIF". So true, but tomorrow we'll make our program interactive with a bit of mouse handling.