Friday, September 10, 2010

Throbber for Java

Have you ever wanted to do a please wait for a Java application?  Did you want a little spinny logo to animate while they waited?

As Professor Fransworth would say, "Good news everybody!"

Let's start out with a preview of the graphic that I'll be using.
Spinner Image

Now this is actually nine different images, I've just haphazardly put them into one image here so you can see what I'm doing. I made these images in about five minutes using Inkscape but you can use whatever. You do need to export the images into a format that Java understands, I usually default to png.

I've named the rotor images rotor1.png through rotor8.png and the final image (the check mark) as rotord.png. The check mark is optional and the code below is clear cut enough to be able to remove the check mark. At any rate I'm placing the images into a package called sample.img

Now here's what we need to do. We need to create a custom JFrame (of Swing fame) that will be our spinning logo. IF you are using Netbeans, DO NOT USE NEW JFrame CLASS. If you are using Netbeans select Create New Java class. This gives you a no frills attached blank document with a shell of a Java class to begin with. If you do use the JFrame class in Netbeans it adds an XML document behind the scenes that allows you to use the GUI-fied editor, which will only serve to get in your way, in this case at least.

So let's build our customer JFrame.



package sample.ui;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import javax.swing.JPanel;

/**
*
* @author John Doe
*/
public class RotorPanel extends JPanel {

private Image[] rotorImg;
private Image rotorComplete;
private boolean done;
private int current = 0;
private final int max_image = 7;

public RotorPanel() {
this.rotorImg = new Image[max_image+1];
for(int i=0; i<max_image+1; i++)
this.rotorImg[i] = Toolkit.getDefaultToolkit().createImage(getClass().getResource("/sample/img/rotor" + (i+1) + ".png"));
this.rotorComplete = Toolkit.getDefaultToolkit().createImage(getClass().getResource("/sample/img/rotord.png"));
this.done = false;
}

@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
if(!done)
g2.drawImage(this.rotorImg[this.current], 0, 0, this);
else
g2.drawImage(this.rotorComplete, 0, 0, this);
}

public void increment() {
this.current++;
if(this.current>this.max_image)
this.current = 0;
}

public void setDone(boolean done) {
this.done = done;
}

public boolean isDone() {
return done;
}

}



As you can see it is pretty straight forward in approach. The constructor loads the images into an array and sets the done flag to false. The increment method moves us through the images. We've overridden the paint method to draw the image.

Now let's use our JPanel. If you are using Netbeans, go ahead an use the JDialog template, that's what I'll be covering here. In Netbeans add a standard JPanel from the palette and then in the properties window click on the code tab. In the custom creation code add "new RotorPanel()". This replaces the new javax.swing.JPanel that is found in the protected area (which is controlled by the XML file behind the scenes) with this code, which is what we want.

Next in the constructor code right after the initComponents call add the following code.


this.t = new Thread() {
@Override
public void run() {
RotorPanel p = (RotorPanel) PleaseWaitDialog.this.jPanel1;
while(!p.isDone()) {
p.increment();
p.repaint();
try { Thread.sleep(65); }
catch (InterruptedException ex) {}
}
p.repaint();
}
};


This creates a thread that will call the increment method to rotate the image and then the repaint method which will update our image. The Thread.sleep(65) call is to sleep 65 ms and then repaint the image, this roughly controls the speed of the animation. Notice that the loop checks the done flag. When you are done and want this thread to die, simply call ((RotorPanel) this.jPanel1).setDone(true);

In 65 ms or less the thread will die because we exit the while loop and the run method of the thread returns. Of course this doesn't mean that you need to stop displaying something, just that you will no longer be rotating the image, hence the check mark image. The actual image doesn't go away until you kill the JDialog.

Pretty simple animation. If you want better control over animation and what-not, I would suggest looking into the timing framework that is located at https://timingframework.dev.java.net/. This framework will allow you have a better control over animating something. However, this is a simple little snippit of code that you can use for simple animations.

Cheers!

Powered by ScribeFire.

No comments: