Saturday, February 19, 2011

Java : When You've Got Timing

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.

19 comments:

  1. I'm liking it--even the commentary is entertaining. People might think it's too much work for just a picture like you said but it's not too much more work to get it to do whatever you want.

    Thanks for making programming less heavy... Your choice of pictures is so great too. It's funny, even though I studied CSC I haven't programmed in ages.

    ReplyDelete
  2. I need to put more time into learning the basics I think, but your work here is not under-appreciated good sir.

    ReplyDelete
  3. thanks for the awesome tutorial :D

    ReplyDelete
  4. good tutorial. and i doubt bambi would be stupid enough to fall in love with hitler.

    ReplyDelete
  5. Who knew learning would be so hard?

    Still trying, though.

    ReplyDelete
  6. Great tutorial, unfortunately I have no use for it

    ReplyDelete
  7. TMH=Too Much Herb

    Ill have to come back to this

    ReplyDelete
  8. What's Java... And why does looking at it hurt my thinking parts??

    ReplyDelete
  9. aite i think im the one going crazy, its okay though

    ReplyDelete
  10. i don't like thew way java handballs swing events.

    ReplyDelete
  11. Haven't been keeping up, but def interested in learning coding. Was trying to learn C++ and this language is fairly similar (compared to PHP or VB at the very least)

    ReplyDelete
  12. Thanks for the quick tutorial!

    Following & Supporting
    -Easy

    ReplyDelete